Wednesday, 24 May 2017

How to untar a file - Java Program

This post shows how to untar a tar file. It has both the steps to first uncompress a .tar.gz file and later untar it.

Using Apache Commons Compress

Apache Commons Compress library is used in the code for untarring a file. You can download it from here – https://commons.apache.org/proper/commons-compress/download_compress.cgi.

Make sure to add commons-compress-xxx.jar in your application’s class path. I have used commons-compress-1.13 version.

Example Code

This Java program has two methods deCompressGZipFile() method is used to uncompress a .tar.gz file to get a .tar file. Using unTarFile() method this .tar file is untarred.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;

public class UnTarDemo {

    public static void main(String[] args) {
        // Path to input file, which is a 
        // tar file compressed to create gzip file
        String INPUT_FILE = "G:\\Test.tar.gz";
        // This folder should exist, that's where
        // .tar file will go
        String TAR_FOLDER = "G:\\TarFile";
        // After untar files will go to this folder
        String DESTINATION_FOLDER = "G:\\Temp";
        UnTarDemo unTarDemo = new UnTarDemo();
        try {
            File inputFile = new File(INPUT_FILE);
            String outputFile = getFileName(inputFile, TAR_FOLDER);
            System.out.println("outputFile " + outputFile);
            File tarFile = new File(outputFile);
            // Calling method to decompress file
            tarFile = unTarDemo.deCompressGZipFile(inputFile, tarFile);
            File destFile = new File(DESTINATION_FOLDER);
            if(!destFile.exists()){
                 destFile.mkdir();
            }
            // Calling method to untar file
            unTarDemo.unTarFile(tarFile, destFile);
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    /**
     * 
     * @param tarFile
     * @param destFile
     * @throws IOException
     */
    private void unTarFile(File tarFile, File destFile) throws IOException{
        FileInputStream fis = new FileInputStream(tarFile);
        TarArchiveInputStream tis = new TarArchiveInputStream(fis);
        TarArchiveEntry tarEntry = null;
        
        // tarIn is a TarArchiveInputStream
        while ((tarEntry = tis.getNextTarEntry()) != null) {
            File outputFile = new File(destFile + File.separator + tarEntry.getName());
            
            if(tarEntry.isDirectory()){
                
                System.out.println("outputFile Directory ---- " 
                    + outputFile.getAbsolutePath());
                if(!outputFile.exists()){
                    outputFile.mkdirs();
                }
            }else{
                //File outputFile = new File(destFile + File.separator + tarEntry.getName());
                System.out.println("outputFile File ---- " + outputFile.getAbsolutePath());
                outputFile.getParentFile().mkdirs();
                //outputFile.createNewFile();
                FileOutputStream fos = new FileOutputStream(outputFile); 
                IOUtils.copy(tis, fos);
                fos.close();
            }
        }
        tis.close();
    }
    
    /**
     * Method to decompress a gzip file
     * @param gZippedFile
     * @param newFile
     * @throws IOException
     */
    private File deCompressGZipFile(File gZippedFile, File tarFile) throws IOException{
        FileInputStream fis = new FileInputStream(gZippedFile);
        GZIPInputStream gZIPInputStream = new GZIPInputStream(fis);
        
        FileOutputStream fos = new FileOutputStream(tarFile);
        byte[] buffer = new byte[1024];
        int len;
        while((len = gZIPInputStream.read(buffer)) > 0){
            fos.write(buffer, 0, len);
        }
        
        fos.close();
        gZIPInputStream.close();
        return tarFile;
                
    }
    
    /**
     * This method is used to get the tar file name from the gz file
     * by removing the .gz part from the input file
     * @param inputFile
     * @param outputFolder
     * @return
     */
    private static String getFileName(File inputFile, String outputFolder){
        return outputFolder + File.separator + 
                inputFile.getName().substring(0, inputFile.getName().lastIndexOf('.'));
    }
}

That's all for this topic How to untar a file - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Creating tar file and GZipping multiple files - Java Program
  2. Zipping files in Java
  3. Unzipping files in Java
  4. Compressing and Decompressing File in GZIP Format
  5. How to read file from the last line in Java

You may also like -

>>>Go to Java Programs page

Monday, 22 May 2017

Creating tar file and GZipping multiple files - Java Program

If you want to GZIP multiple files that can’t be done directly as you can only compress a single file using GZIP.

For that you will have to archive multiple files into a tar and then compress it to create a .tar.gz compressed file.

Using Apache Commons Compress

Here I am posting a Java program to create a tar file using Apache Commons Compress library. You can download it from here – https://commons.apache.org/proper/commons-compress/download_compress.cgi

Make sure to add commons-compress-xxx.jar in your application’s class path. I have used commons-compress-1.13 version.

Steps for creating tar files

Steps for creating tar files are as follows -

  1. Create a FileOutputStream to the output file (.tar.gz) file.
  2. Create a GZIPOutputStream which will wrap the FileOutputStream object.
  3. Create a TarArchiveOutputStream which will wrap the GZIPOutputStream object.
  4. Then you need to read all the files in a folder.
  5. If it is a directory then just add it to the TarArchiveEntry.
  6. If it is a file then add it to the TarArchiveEntry and also write the content of the file to the TarArchiveOutputStream.

Folder Structure used

Here is a folder structure used in this post to read the files. Test, Test1 and Test2 are directories here and then you have files with in those directories. Your Java code should walk through the whole folder structure and create a tar file with all the entries for the directories and files and then compress it.

Test
  abc.txt
  Test1
     test.txt
     test1.txt
  Test2
     xyz.txt

Example code

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;

public class TarGZIPDemo {

 public static void main(String[] args) {
   String SOURCE_FOLDER = "G:\\Test";
   TarGZIPDemo tGzipDemo = new TarGZIPDemo();
   tGzipDemo.createTarFile(SOURCE_FOLDER);
 }
 
 /**
  * 
  * @param source
  */
 private void createTarFile(String source){
  TarArchiveOutputStream tarOs = null;
  try {
    // Using input name to create output name
    FileOutputStream fos = new FileOutputStream(source.concat(".tar.gz"));
    GZIPOutputStream gos = new GZIPOutputStream(new BufferedOutputStream(fos));
    tarOs = new TarArchiveOutputStream(gos);
    File folder = new File(source);
    File[] fileNames = folder.listFiles();
    for(File file : fileNames){
      System.out.println("PATH " + file.getAbsolutePath());
      System.out.println("File name " + file.getName());
      addFilesToTarGZ(file.getAbsolutePath(), file, tarOs);
    }
   
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }finally{
    try {
     tarOs.close();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
 }
 /**
  * 
  * @param source
  * @param file
  * @param tos
  * @throws IOException
  */
 private void addFilesToTarGZ(String source, File file, TarArchiveOutputStream tos) 
                   throws IOException{
   // New TarArchiveEntry
   tos.putArchiveEntry(new TarArchiveEntry(file, source));
   if(file.isFile()){
     FileInputStream fis = new FileInputStream(file);
     BufferedInputStream bis = new BufferedInputStream(fis);
     // Write content of the file
     IOUtils.copy(bis, tos);
     tos.closeArchiveEntry();
     fis.close();
   }else if(file.isDirectory()){
     // no need to copy any content since it is
     // a directory, just close the outputstream
     tos.closeArchiveEntry();
     for(File cFile : file.listFiles()){
       // recursively call the method for all the subfolders
       addFilesToTarGZ(cFile.getAbsolutePath(), cFile, tos);
    
     }
   }
  
 }
}

That's all for this topic Creating tar file and GZipping multiple files - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Zipping files in Java
  2. Unzipping files in Java
  3. Compressing and Decompressing File in GZIP Format
  4. How to convert a file to byte array

You may also like -

>>>Go to Java Programs page

Monday, 1 May 2017

Compressing and Decompressing File in GZIP Format - Java Program

You will mainly use GZIP tool to compress and decompress files in Unix systems. Here note that you can only compress a single file using GZIP not multiple files residing in a folder.

Steps to compress a file

In order to compress a file using GZIP the steps are as follows -

  1. For reading the source file (file which has to be GZIPped) create a FileInputStream.
  2. Create a FileOutputStream to the target file (output GZIPped file).
  3. Create a GZIPOutputStream wrapping the FileOutputStream.
  4. Then you just need to read from the input stream and write to the output stream.

Steps to decompress a file

In order to decompress a file using GZIP the steps are as follows -

  1. For reading the compressed file create a FileInputStream.
  2. Wrap that FileInputStream with in a GZIPInputStream.
  3. Create a FileOutputStream to the new file (created on decompressing GZIP file).
  4. Then you just need to read from the input stream and write to the output stream.

Example code for compressing and decompressing file in GZIP format

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class GZipDemo {

 public static void main(String[] args) {
  // Path to file which is gzipped
  String SOURCE_FILE = "G:\\Test\\abc.txt";
  // Path to gzipped output files
  String GZIP_OUTPUT_FILE = "G:\\Test\\abc.gz";
  // File you get after decompressings
  String GZIP_NEW_FILE = "G:\\Test\\newabc.txt";
  
  GZipDemo gZipDemo = new GZipDemo();
  try {
   // Compressing a file
   gZipDemo.compressGZipFile(SOURCE_FILE, GZIP_OUTPUT_FILE);
   
   // decompressing a file
   gZipDemo.deCompressGZipFile(GZIP_OUTPUT_FILE, GZIP_NEW_FILE);
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }
 /**
  * Method to gzip a file
  * @param sourceFile
  * @param outputFile
  * @throws IOException
  */
 public void compressGZipFile(String sourceFile, String outputFile) 
    throws IOException{
  FileInputStream fis = new FileInputStream(sourceFile);
  FileOutputStream fos = new FileOutputStream(outputFile);
  GZIPOutputStream gZIPOutputStream = new GZIPOutputStream(fos); 
  byte[] buffer = new byte[1024];
  int len;
  while((len = fis.read(buffer)) > 0){
   gZIPOutputStream.write(buffer, 0, len);
  }
  
  fis.close();
  gZIPOutputStream.close();
  
 }
 
 /**
  * Method to decompress a gzip file
  * @param gZippedFile
  * @param newFile
  * @throws IOException
  */
 public void deCompressGZipFile(String gZippedFile, String newFile) 
    throws IOException{
  FileInputStream fis = new FileInputStream(gZippedFile);
  GZIPInputStream gZIPInputStream = new GZIPInputStream(fis);
  FileOutputStream fos = new FileOutputStream(newFile);
  byte[] buffer = new byte[1024];
  int len;
  while((len = gZIPInputStream.read(buffer)) > 0){
   fos.write(buffer, 0, len);
  }
  
  fos.close();
  gZIPInputStream.close();
 }

}

That's all for this topic Compressing and Decompressing File in GZIP Format - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Zipping files in Java
  2. Unzipping files in Java
  3. Reading all files in a folder - Java Program
  4. Writing file in Java

You may also like -

>>>Go to Java Programs page

Wednesday, 26 April 2017

Reading all files in a folder - Java Program

This post is about how to read all the files in a directory. Suppose you have folder with files in it and there are sub-folders with files in those sub-folders and you want to read or list all the files.

Here is a folder structure used in this post to read the files. Test, Test1 and Test2 are directories here and then you have files with in those directories.

Test
  abc.txt
  Test1
  test.txt
  test1.txt
 Test2
  xyz.txt

Example Code

There are two ways to list all the files one is using the listFiles() method of the File class which is there in Java from 1.2.

Another way is using Files.walk() method which is a recent addition in Java 8.

 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;

public class ListFiles {
    public static void main(String[] args) {
      File folder = new File("G:\\Test");
      ListFiles listFiles = new ListFiles();
      System.out.println("reading files before Java8 - Using listFiles() method");
      listFiles.listAllFiles(folder);
      System.out.println("-------------------------------------------------");
      System.out.println("reading files Java8 - Using Files.walk() method");
      listFiles.listAllFiles("G:\\Test");

     }
         
     public void listAllFiles(File folder){
         System.out.println("In listAllfiles(File) method");
         File[] fileNames = folder.listFiles();
         for(File file : fileNames){
             // if directory call the same method again
             if(file.isDirectory()){
                 listAllFiles(file);
             }else{
                 try {
                     readContent(file);
                 } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
        
             }
         }
     }
         
     public void listAllFiles(String path){
         System.out.println("In listAllfiles(String path) method");
         try(Stream<Path> paths = Files.walk(Paths.get(path))) {
             paths.forEach(filePath -> {
                 if (Files.isRegularFile(filePath)) {
                     try {
                         readContent(filePath);
                     } catch (Exception e) {
                         // TODO Auto-generated catch block
                         e.printStackTrace();
                     }
                 }
             });
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } 
     }
     
     public void readContent(File file) throws IOException{
         System.out.println("read file " + file.getCanonicalPath() );
         try(BufferedReader br  = new BufferedReader(new FileReader(file))){
               String strLine;
               // Read lines from the file, returns null when end of stream 
               // is reached
               while((strLine = br.readLine()) != null){
                System.out.println("Line is - " + strLine);
               }
              }
     }
     
     public void readContent(Path filePath) throws IOException{
         System.out.println("read file " + filePath);
         List<String> fileList = Files.readAllLines(filePath);
         System.out.println("" + fileList);
     }
     
}

Output

reading files before Java8 - Using listFiles() method
In listAllfiles(File) method
read file G:\Test\abc.txt
Line is - This file is in Test folder.
In listAllfiles(File) method
read file G:\Test\Test1\test.txt
Line is - This file test is under Test1 folder.
read file G:\Test\Test1\test1.txt
Line is - This file test1 is under Test1 folder.
In listAllfiles(File) method
read file G:\Test\Test2\xyz.txt
Line is - This file xyz is under Test2 folder.
-------------------------------------------------
reading files Java8 - Using Files.walk() method
In listAllfiles(String path) method
read file G:\Test\abc.txt
[This file is in Test folder.]
read file G:\Test\Test1\test.txt
[This file test is under Test1 folder.]
read file G:\Test\Test1\test1.txt
[This file test1 is under Test1 folder.]
read file G:\Test\Test2\xyz.txt
[This file xyz is under Test2 folder.]

Here we have two overloaded methods listAllFiles(). First one take File instance as argument that one is used to read files using the File.listFiles() method. In that method while going through the list of files under a folder you check if the next element of the list is a file or a folder. If it is a folder then you recursively call the listAllFiles() method with that folder name. If it is a file you call the readContent() method to read the file using BufferedReader.

Another version of listAllFiles() method takes String as argument. In this method whole folder tree is traversed using the Files.walk() method. Here again you verify if it is a regular file then you call the readContent() method to read the file.

Note that readContent() method is also overloaded one takes File instance as argument and another Path instance as argument.

That's all for this topic Reading all files in a folder - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Writing file in Java
  2. How to append to a file in Java
  3. How to read file from the last line in Java
  4. How to find last modified date of a file in Java
  5. Unzipping files in Java

You may also like -

>>>Go to Java Programs page

Monday, 24 April 2017

Object cloning in java

In Java if you assign an object variable to another variable the reference is copied which means both variable will share the same reference. In this case any change in one object variable will be reflected in another.

As example – If there is a class Test and you create an object of that class and then assign that reference to another object variable.

Test obj1 = new Test();
Test obj2 = obj1;

Here both obj1 and obj2 will have the same reference.

Object cloning

What is the option then if you want to quickly create an object using the existing object in such a way that you get a new instance (reference is not shared) with the same content for the fields in the new object as in existing object.

That’s when you can use clone() method which creates an exact copy of the existing object. Then you can modify the cloned object without those modification reflecting in original object (Well we’ll go into shallow copy and deep copy a little later).

clone() method

clone() method is defined as protected in the Object class which you must override as public in any derived classes that you want to clone.

Signature of clone method in Object class

protected native Object clone() throws CloneNotSupportedException;

Process of cloning

There are two required steps if you want to clone any object.

  1. You need to call clone() method of the Object class or provide your own implementation by overriding the clone() method in your class.
  2. Your class, whose object you want to clone, must implement Cloneable interface which is part of java.lang package. Not implementing Cloneable interface will result in CloneNotSupportedException exception being thrown when clone method is called.

Here note that Cloneable interface is a marker interface and defines no members of its own.

Scenario 1- Calling clone() method from a method

Here we have a class Test which implements Cloneable interface and it has a method cloneIt() which calls the clone() method of the Object class.

Class Test

public class Test implements Cloneable{
  int a;
  float f;
 
 Test cloneIt(){
  Test test = null;
  try {
   // Calling clone() method of Object class
   test = (Test)super.clone();
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return test;
  
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  Test t1 = new Test();
  t1.a = 10;
  t1.f = 13.4F;
  // Calling method to clone
  Test t2 = t1.cloneIt();
  System.out.println("t1.a " + t1.a + " t1.f " + t1.f);
  
  System.out.println("t2.a " + t2.a + "t2.f " + t2.f);

  if(t1 != t2){
   System.out.println("Different instances");
  }else{
   System.out.println("Same instances");
  }
 }

}

Output

t1.a 10 t1.f 13.4
t2.a 10t2.f 13.4
Different instances

Here you can see that Test class object t1 is cloned and a new instance t2 is created, in the code even reference equality is checked and you can see that the reference is not shared and both are indeed different instances.

Points to note

Some of the points to note from this code –

  1. Class whose object has to be cloned should implement Cloneable interface, otherwise java.lang.CloneNotSupportedException exception will be thrown.
  2. clone() method is a protected method in Object class, since all the classes inherit from Object class so you can call the protected method of the super class.
  3. While cloning bitwise copy of the object is created.

Scenario 2 – Overriding clone method

Another way to provide clone functionality is to override the clone() method in the class in that case it has to be a public method in order to be accessible.

If we change the classes used in the above example to have overridden clone method and calling that clone method then the ' structure will be as follows –

Test class

public class Test implements Cloneable{
 int a;
 float f;
 // Override clone method
 public Object clone(){
  Object obj = null;
  try {
   // Calling clone() method of Object class
   obj = super.clone();
   } catch(CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  return obj;
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  Test t1 = new Test();
  t1.a = 10;
  t1.f = 13.4F;
  // Call clone method
  Test t2 = (Test)t1.clone();
  System.out.println("t1.a " + t1.a + " t1.f " + t1.f);
  
  System.out.println("t2.a " + t2.a + " t2.f " + t2.f);
  
  if(t1 != t2){
   System.out.println("Different instances");
  }else{
   System.out.println("Same instances");
  }
 }
}

Output

t1.a 10 t1.f 13.4
t2.a 10 t2.f 13.4
Different instances

Advantages of cloning

If you have an object, creation of which using the usual way is costly; as example if you have to call DB in order to get data to create and initialize your object. In that scenario rather than hitting DB every time to create your object you can cache it, clone it when object is needed and update it in DB only when needed.

Actually there is a design pattern called prototype design pattern which suggests the same approach.

Shallow copy

In the above examples only primitive types are used so there is no problem, if you change any primitive value that won’t reflect in other object.

What if there is another object reference in your class? As already mentioned when you clone an object all the values for the fields are copied to the cloned object. Since Java is pass by value, if the field value is a reference to an object (a memory address) it copies that reference to the field of the cloned object. In that case referenced field is shared between both objects and any change made to the referenced field will be reflected in the other object too.

This process of cloning when the field values are copied to the new object is known as shallow copy. Shallow copies are simple to implement and typically cheap, as they can be usually implemented by simply copying the bits exactly.

Example code

Let’s try to clarify it with an example. Here we have a Class called ClassA with 2 int fields. Another class ClassB which has a ClassA object and an int field.

Then you create an object objB of ClassB and then clone it to get a new object objB2. In both of these objects reference of ClassA object will be shared.

ClassA

public class ClassA {
 private int i;
 private int j;
 // Constructor
 public ClassA(int i, int j){
  this.i = i;
  this.j = j;
 }
 public void setI(int i) {
  this.i = i;
 }
 public void setJ(int j) {
  this.j = j;
 }
 public int getI() {
  return i;
 }
 public int getJ() {
  return j;
 }
}

ClassB

public class ClassB implements Cloneable{
 private int x;
 private ClassA objA;
 
 public ClassB(int x, ClassA objA){
  this.x = x;
  this.objA = objA;
 }
 public Object clone() throws CloneNotSupportedException{
  return super.clone();
 }
 public int getX() {
  return x;
 }
 public ClassA getObjA() {
  return objA;
 }
 public void setX(int x) {
  this.x = x;
 }
 public void setObjA(ClassA objA) {
  this.objA = objA;
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  ClassB objB = new ClassB(10, new ClassA(20, 30));
  
  ClassB objB2 = null;
  try {
   objB2 = (ClassB)objB.clone();
   
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  // value of field i of objA changed 
  // in cloned object
  objB2.getObjA().setI(100); 
  // Value of primitive field x changed 
  // in cloned object
  objB2.setX(1);
  System.out.println("objB.x- " +  objB.getX() + " objB.objA.i- " + objB.getObjA().getI() + " objB.objA.j- " + objB.getObjA().getJ());
  System.out.println("objB2.x- " +  objB2.getX() + " objB2.objA.i- " + objB2.getObjA().getI() + " objB2.objA.j- " + objB2.getObjA().getJ());

 }
}

Output

objB.x- 10 objB.objA.i- 100 objB.objA.j- 30
objB2.x- 1 objB2.objA.i- 100 objB2.objA.j- 30 

In CloningDemo class objB is cloned to get a new instance objB2. Value of primitive field x is changed in the cloned object objB2, you can see that both objects have their own independent values for field x.

Coming to object field, objB2 will have its own field objA where value of field i is changed. You can see that value in the original object objB for objA.i is also changed as the objA reference is shared between the objects. That’s one drawback of shallow copy.

Deep Copy

If you don’t want references of object being copied during the cloning process then option is deep copy. When a deep copy is done objects referenced by the cloned object are distinct from those referenced by original object, and independent.

Deep copies are more expensive, as you need to create additional objects, and can be substantially more complicated, due to references possibly forming a complicated graph.

Example Code

public class ClassB implements Cloneable{
 private int x;
 private ClassA objA;
 
 public ClassB(int x, ClassA objA){
  this.x = x;
  this.objA = objA;
 }
 public Object clone() throws CloneNotSupportedException{
  // Cloning object
  ClassB objB = (ClassB)super.clone();
  // Explicitly assigning reference to new ClassA object
  objB.setObjA(new ClassA(100, objA.getJ()));
  return objB;
 }
 public int getX() {
  return x;
 }
 public ClassA getObjA() {
  return objA;
 }
 public void setX(int x) {
  this.x = x;
 }
 public void setObjA(ClassA objA) {
  this.objA = objA;
 }
}

Here ClassB is changed to create a deep copy while cloning the object. Notice in the clone method, for the cloned object a new ClassA object is created with a new value(100) for field i and keeping the old value for field j. Since it’s a new object so reference is not shared with the classA object of the original object.

If we run this code now using the below program -

public class CloningDemo {

 public static void main(String[] args) {
  ClassB objB = new ClassB(10, new ClassA(20, 30));
  
  ClassB objB2 = null;
  try {
   objB2 = (ClassB)objB.clone();
   
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  // value of field i of objA changed 
  // in cloned object
  //objB2.getObjA().setI(100); 
  // Value of primitive field x changed 
  // in cloned object
  objB2.setX(1);
  System.out.println("objB.x- " +  objB.getX() + " objB.objA.i- " + objB.getObjA().getI() 
     + " objB.objA.j- " + objB.getObjA().getJ());
  System.out.println("objB2.x- " +  objB2.getX() + " objB2.objA.i- " + objB2.getObjA().getI() 
    + " objB2.objA.j- " + objB2.getObjA().getJ());

 }

}

Output

objB.x- 10 objB.objA.i- 20 objB.objA.j- 30
objB2.x- 1 objB2.objA.i- 100 objB2.objA.j- 30

Now you can see that value for field i is changed only in cloned object not in the original object.

That's all for this topic Object cloning in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Serialization in Java
  2. serialVersionUID and versioning in Java Serialization
  3. Marker interface in Java

You may also like -

>>>Go to Java advance topics page


Saturday, 22 April 2017

Serialization Proxy Pattern in Java

When you serialize an object in Java it is converted to byte stream and object is reconstituted using that byte stream during the process of deserialization.

Sometimes this extraneous behavior of creating object using the byte stream is not what you want and you still want constructor (or any other method if required) to be called when your object is created during the process of deserialization.

Serialization proxy pattern

Serialization proxy pattern is a way to design your class where proxy pattern defines its serialization mechanism.

Before going into any more details about serialization proxy pattern let’s know about two methods.

  • writeReplace()
  • readResolve()

writeReplace() method

Serializable classes that use an alternative object (proxy object) when writing an object to the stream should implement writeReplace() method with the exact signature:

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

This writeReplace() method is invoked by serialization if the method exists and this method is defined within the original class whose object is serialized. Thus, the method can have private, protected and package-private access modifier.

readResolve() method

Classes that need to provide a replacement object when the serialized object is read from the stream should implement readResolve() method with the exact signature.

 ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

This readResolve() method follows the same invocation rules and accessibility rules as writeReplace.

How Serialization Proxy pattern works

Rather than serializing the original class you provide functionality using the writeReplace() method to serialize the proxy class instead. Here note that writeReplace() method is implemented in the original class.

At the time of deserialization proxy object is deserialized and then the readResolve() method is called. That’s where you will have to provide the functionality to create the original class object regular way. Here note that readResolve() method is implemented in the proxy class.

Proxy class

Generally serialization proxy pattern is implemented by creating a proxy class as a nested static class with in the original class. Since it needs access to the fields of the outer class so that it is better to create proxy class as a nested class.

Example code

Time is right to see an example of the serialization proxy pattern. Here we have a Person class which has a constructor with args. When a Person class object is created it is initialized using this constructor and that’s what you want to do when you deseliarize a serialized Person class object.

For doing that you will use Serialization proxy pattern and create a proxy class (called PersonProxy here) as a nested static class. You will also implement writeReplace() and readResolve() methods.

Person class

import java.io.Serializable;

public class Person implements Serializable{
 
 /**
  * 
  */
 private static final long serialVersionUID = 9140203997753929147L;
 private String name;
 private int id;
 private int age;
 
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
 /**
  * writeReplace method for the proxy pattern
  * @return
  */
 private Object writeReplace() {
  System.out.println("In writeReplace() method");
  return new PersonProxy(this);
 }
 // Nested static class - Proxy
 private static class PersonProxy implements Serializable {
  /**
   * 
   */
  private static final long serialVersionUID = -5965328891170223339L;
  private String name;
  private int id;
  private int age;
  PersonProxy(Person p) {
   this.name = p.name;
   this.id = p.id;
   this.age = p.age;
  }
  // readResolve method for Person.PersonProxy
  private Object readResolve() {
   System.out.println("In readResolve() method");
   return new Person(name, id, age); // Uses public constructor
  } 
 }
}

Util class

A util class with methods to serialize and deserialize.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Util {
  /**
   * Method used for serialization
   * @param obj
   * @param fileName
   */
  public static void serialzeObject(Object obj, String fileName){
   try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){
    oos.writeObject(obj);
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  
  /**
   * Method used for deserializing
   * @param fileName
   * @return
   * @throws ClassNotFoundException
   */
  public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
   Object obj = null;
   try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
    obj = ois.readObject();
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return obj;
  }
}

SerializationDemo class

Using this class a Person class object will be serialized and later deserialized.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    System.out.println("About to serialize ....");
    // serializing
    Util.serialzeObject(person, fileName);
    
    try {
     System.out.println("About to deserialize ....");
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}

Output

In Constructor with args
About to serialize ....
In writeReplace() method
About to deserialize ....
In readResolve() method
In Constructor with args
id 1 Name User1 Age 22

Here you can see that writeReplace() method is called when object is serialized. At the time of deserializing the object readResolve() method is called where object is created and initialized using the constructor of the class not just recreated using the byte stream.

Creating instance by getting data from DB

Another example, which you will see in many frameworks too is when you want your instance to be created using a DB call. In that case what you need to serialize is some identifier only and during deserialization using that identifier you will get the data to construct the object from DB.

Example Code

Here you have a Person class with fields as id, name etc. In writeReplace() method where you serialize the proxy you provide the id also.

Person class

public class Person implements Serializable {
     private int id;
 private String name;
 …
 private Object writeReplace() {
  return new PersonProxy(id);
 }
}

Proxy Class

In the readResolve() method of the proxy class you create the Person class instance using the id which you saved earlier.

public class PersonProxy implements Serializable {
 private int id;
 public PersonProxy(int id) {
  this.id = id;
 }
 public Object readResolve() {
  // DB call to get the person record by id
  return PersonDAO.findById(id);
 }
}

That's all for this topic Serialization Proxy pattern in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. serialVersionUID and versioning in Java Serialization
  2. Serialization in Java
  3. Externalizable interface in Java
  4. Transient in Java
  5. Insert\Update using NamedParameterJDBCTemplate in Spring framework
  6. Select query using NamedParameterJDBCTemplate in Spring framework

You may also like -

>>>Go to Java advance topics page

Wednesday, 19 April 2017

serialVersionUID and versioning in Java Serialization

If you ever implemented Serializable interface then you would have seen the warning “The serializable class XXX does not declare a static final serialVersionUID field of type long”. If you did wonder why this warning then this post will help to understand it.

SerialVersionIUID and versioning

A simple explanation for why do we need to declare serialVersionUID is it helps with versioning.

Suppose you have some class which is serialized and it changes before it is deserialized. You will need to consider what happens in that situation? Can you allow the new version of your class to read old data.

To help with these versioning scenarios serialization process provides a simple versioning mechanism using serialVersionUID.

serialVerionUID generation

The stream-unique identifier is a 64-bit hash of the class name, interface class names, methods, and fields. If you are using IDE like eclipse and you have a class that implements Serializable interface then you will get a warning upfront that serialVersionUID is not declared.

Eclipse will also give you options -

  • to add default serialVersionUID
  • OR
  • to add generated serialVersionUID

In case you choose to ignore that warning even then by default serialization mechanism will generate serialVersionUID, both the name of the class and its serialVersionUID are written to the object stream.

During deserialization again serialVersionUID will be generated and compared with the previously written serialVersionUID, if there is a mismatch that means version is changed and InvalidClassException will be thrown.

Example code

Let’s try to clear it with an example. Suppose you have a Person class with few fields and you serialize the Person class. Now you add a new field in Person and try to deserialize it.

Person class

public class Person implements Serializable{
 private String name;
 private int id;
 private int age;
 /*private String city;
 public String getCity() {
  return city;
 }*/
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

So here is a Person class with fields like id, name and age. It implements Serializable interface and choose to ignore the warning to declare serialVersionUID.

Util class

This is a class with static methods to serialize and deserialize.

public class Util {
  /**
   * Method used for serialization
   * @param obj
   * @param fileName
   */
  public static void serialzeObject(Object obj, String fileName){
   try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){
    oos.writeObject(obj);
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  
  /**
   * Method used for deserializing
   * @param fileName
   * @return
   * @throws ClassNotFoundException
   */
  public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
   Object obj = null;
   try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
    obj = ois.readObject();
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return obj;
  }
}

Now I serialize Person class object using the following test class.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    Util.serialzeObject(person, fileName);
    
    /*try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }*/
 }
}

So far so good, Person class object is created and serialized. Now what you do is to add a new field to Person class.

public class Person implements Serializable{
 private String name;
 private int id;
 private int age;
 private String city;
 
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
 public String getCity() {
  return city;
 }
}

Here you can see a new field city is added to a Person class.

Now if I try to deserialize the byte stream which was already created before the inclusion of this new field in Person class InvalidClassException will be thrown.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}
java.io.InvalidClassException: org.netjs.prog.Person; local class incompatible: stream classdesc 
serialVersionUID = -4901887311122736183, local class serialVersionUID = -1818819755742473032
 at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
 at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
 at java.io.ObjectInputStream.readClassDesc(Unknown Source)
 at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
 at java.io.ObjectInputStream.readObject0(Unknown Source)
 at java.io.ObjectInputStream.readObject(Unknown Source)
 at org.netjs.prog.Util.deSerialzeObject(Util.java:39)
 at org.netjs.prog.SerializationDemo.main(SerializationDemo.java:15)

Here note that even if you chose to ignore the warning and didn’t declare the serialVersionUID it is still generated. You can see that 2 different serialVersionUIDs are there as the class has changed and that mismatch has caused the exception.

Assigning serialVersionUID

As shown in the above example if you choose to ignore the warning and rely on generation of serialVersionUID by the serialization mechanism itself it will always fail if there is a change in the class.

That’s why you as an implementor of the class should take charge and assign a serialVersionUID yourself (If you are using IDE like Eclipse that can be generated by Eclipse for you or you can use serialver tool which comes with the JDK to generate serialVersionUID).

With you taking charge when you know your class has changed in a way that it is not compatible with the old version anymore you can choose to change the serialVersionUID. In that case during deserialization because of the non-matching serialVersionUID, InvalidClassException will be thrown.

If you choose not to change the serialVersionUID even if your class has changed as you think the change is not significant then deserialization will proceed with out throwing any exception.

Example code

Let’s take the same example as above but this time serialVersionUID is declared.

Person class

public class Person implements Serializable{
 /**
  * 
  */
 private static final long serialVersionUID = -4046333379855427853L;
 private String name;
 private int id;
 private int age;
 /*private String city;
 public String getCity() {
  return city;
 }*/
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

Now the class is serialized.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    /*try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge() + " City " + person.getCity());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }*/
 }
}

Now a new field city is added in the Person class but the serialVersionUID remains same as before.

public class Person implements Serializable{
 /**
  * 
  */
 private static final long serialVersionUID = -4046333379855427853L;
 private String name;
 private int id;
 private int age;
 private String city;
 public String getCity() {
  return city;
 }
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

Now deserialization will happen though there won’t be any value for the city field.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge() + " City " + person.getCity());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}

Output

In Constructor with args
id 1 Name User1 Age 22 City null

For test you can regenerate the serialVersionUID of the Person class after adding city field. In that case deserialization will fail as there will be a mismatch between serialVersionUIDs.

Points to remember

  1. serialVersionUID is used for versioning of the serialized streams. During serialization process serialVersionUID is also stored. During deserialization generated serialVersionUID is matched with the stored one and if there is a mismatch process fails.
  2. serialVersionUID is a 64-bit hash of the class name, interface class names, methods, and fields. If you don’t declare one yourself serialization process will still generate serialVersionUID. In that case it will fail for any change in the class.
  3. If you declare the serialVersionUID that gives you control over the versioning. When you think class has grown in way that is not compatible with the previous versions then you can change the serialVersionUID. If you think change in the class are not significant enough to change the serialVersionUID you may choose to retain the same serialVersionUID. In that case serialization and deserialization will not fail even if your class had changed.
  4. serialVersionUID is declared as a private static final long and it is always better to declare one in order to have control over the versioning of the class.

That's all for this topic serialVersionUID and versioning in Java Serialization. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Serialization in Java
  2. Transient in Java
  3. Externalizable interface in Java
  4. Serialization Proxy Pattern in Java
  5. Marker interface in Java

You may also like -

>>>Go to Java advance topics page