eu.optimis.ics.ImageCreationServiceREST.java Source code

Java tutorial

Introduction

Here is the source code for eu.optimis.ics.ImageCreationServiceREST.java

Source

/* $Id: ImageCreationServiceREST.java 11547 2013-02-19 17:34:23Z sulistio $ */

/*
 * Copyright 2011 University of Stuttgart
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.optimis.ics;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
// import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
// import java.util.ResourceBundle;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.UUID;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import com.sun.jersey.multipart.FormDataParam;
import com.sun.jersey.spi.resource.Singleton;

import eu.optimis.ics.core.Constants;
import eu.optimis.ics.core.ImageCreationService;
import eu.optimis.ics.core.exception.ImageNotFoundException;
import eu.optimis.ics.core.exception.StateChangeException;
import eu.optimis.ics.core.exception.UnsupportedStateException;
import eu.optimis.ics.core.image.Image;
import eu.optimis.ics.core.image.ImageType;
import eu.optimis.ics.core.io.FileUtils;
import eu.optimis.ics.core.util.PropertiesReader;

/**
 * Image Creation Service REST API
 * 
 * @author Roland Kuebert (kuebert@hlrs.de)
 * @author Tinghe Wang (twang@hlrs.de)
 * @author Anthony Sulistio (sulistio@hlrs.de)
 * 
 */
@Path("/")
@Singleton
public class ImageCreationServiceREST {
    /** An instance of the ICS Core object that does all the functionalities */
    private static ImageCreationService ics;

    /** A logger */
    private final static Logger LOGGER = Logger.getLogger(ImageCreationServiceREST.class.getName());

    /** 
     * A temporary directory, where uploaded files (e.g. txt, zip or war) will be stored to initially.
     * It is defined in the ICS properties file.
     */
    private String sourceDirectory;

    /** A directory to store the uploaded files in the image, defined in the ICS properties file */
    private String targetDirectory;

    /** A Tomcat webapps directory to store the war files */
    private String webappDirectory;

    /**
     * A ICS REST Constructor
     */
    public ImageCreationServiceREST() {
        LOGGER.debug("ics.REST is invoked");
        if (ics == null) {
            ics = ImageCreationService.getInstance();
        }

        PropertyConfigurator.configure(PropertiesReader.getConfigFilePath(Constants.LOG4J_CONFIG_FILE));
        //ResourceBundle rb = ResourceBundle.getBundle("ics");  // old approach
        PropertiesConfiguration rb = PropertiesReader.getPropertiesConfiguration(Constants.ICS_CONFIG_FILE);
        sourceDirectory = rb.getString(Constants.SOURCE_DIRECTORY_PROPERTY);
        targetDirectory = rb.getString(Constants.TARGET_DIRECTORY_PROPERTY);
        webappDirectory = null;

        // Debugging info
        LOGGER.debug("ics.REST: source dir = " + sourceDirectory);
        LOGGER.debug("ics.REST: target dir = " + targetDirectory);
    }

    /**
     * Lists all available images
     * 
     * @return a string listing the available images
     */
    @GET
    @Produces("text/plain")
    @Path("/image")
    public String listImages() {
        LOGGER.debug("ics.REST.listImages(): GET /image - returning a list of all images");

        ArrayList<Image> images = ics.getImages();
        int capacity = 100 * images.size();
        StringBuffer result = new StringBuffer(capacity);
        for (Image image : images) {
            result.append(image.toTextPlain());
        }
        return result.toString();
    }

    /**
     * Lists all base images
     * 
     * @return a string listing the available template or base images
     */
    @GET
    @Produces("text/plain")
    @Path("/baseimage")
    public String listBaseImages() {
        LOGGER.debug("ics.REST.listBaseImages(): GET /baseimage - returning a list of all base images");

        ArrayList<Image> images = ics.getBaseImages();
        int capacity = 100 * images.size();
        StringBuffer result = new StringBuffer(capacity);
        for (Image image : images) {
            result.append(image.toTextPlain());
        }
        return result.toString();
    }

    /**
     * Updates the base image database. This is done by reading from a CSV file
     * defined in the ICS properties file and cleaning up the existing database information.
     * 
     * @return a list of updated base images
     */
    @GET
    @Produces("text/plain")
    @Path("/updatebaseimage")
    public String updateBaseImage() {
        LOGGER.debug(
                "ics.REST.updateBaseImage(): GET /updatebaseimage - updating base image db & returning a list of all base images");

        ics.updateBaseImageDB();
        ArrayList<Image> images = ics.getBaseImages();
        int capacity = 100 * images.size();
        StringBuffer result = new StringBuffer(capacity);
        for (Image image : images) {
            result.append(image.toTextPlain());
        }
        return result.toString();
    }

    /**
     * Lists an information of a given image ID
     * 
     * @param imageId   the image ID
     * @return an information of a single image
     */
    @GET
    @Produces("text/plain")
    @Path("/image/{id}")
    public String listImage(@PathParam("id") String imageId) {
        LOGGER.debug("ics.REST.listImage(): Listing image with id '" + imageId + "'");
        Image image;
        try {
            image = ics.getImage(imageId.trim());
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.listImage(): IOException looking for image", ioException);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.error("ics.REST.listImage(): Image with id " + imageId + " not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        //return image.toTextPlain();
        return image.detailedInfo();
    }

    /**
     * Lists an information of a given base image ID
     * 
     * @param imageId   the image ID
     * @return an information of a single base image
     */
    @GET
    @Produces("text/plain")
    @Path("/baseimage/{id}")
    public String listBaseImage(@PathParam("id") String imageId) {
        LOGGER.debug("ics.REST.listBaseImage(): Listing image with id '" + imageId + "'");
        Image image;
        try {
            image = ics.getBaseImage(imageId.trim());
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.listBaseImage(): IOException looking for image", ioException);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.error("ics.REST.listBaseImage(): Image with id " + imageId + " not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        //return image.toTextPlain();
        return image.detailedInfo();
    }

    /**
     * Creates a new image according to the below image type:
     * <ul>
     * <li>CoreElement</li>
     * <li>OrchestrationElement</li>
     * <li>ImageTemplate : according to the image requirement (described in the XML format)</li>
     * </ul>
     * 
     * For the image requirement, the XML format looks like below:
     * <pre>
     * <ImageTemplate>
     *     <operatingSystem>CentOS</operatingSystem>  <!-- for a wildcard usage, write: Linux -->
     *     <osVersion>5.8</osVersion>      
     *     <architecture>i386</architecture>    <!-- value only i386 or x86_64 -->
     *     <imageSize>10</imageSize>    <!-- in GB (integer only) -->
     * </ImageTemplate>
     * </pre>
     * Note that the above tags are all optional and can be omitted.
     * If this is the case, the ICS will randomly select the eligible base image.
     * 
     * @param imageType     the image type
     * @return the newly-created image ID or 
     *          an exception error if it receives an invalid image type
     */
    @POST
    @Path("/image")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public String deploy(String imageType) {
        LOGGER.debug("ics.REST.deploy(): Received input: " + imageType);
        String ret = "";
        ImageType type = null;

        if (imageType.startsWith("<ImageTemplate>") == true) {
            //LOGGER.info("ics.REST.deploy(): Received a service manifest");
            return createImageFromRequirement(imageType);
        } else if (imageType.equals(ImageType.CoreElement.toString())) {
            LOGGER.info("ics.REST.deploy(): Creating core image");
            type = ImageType.CoreElement;
        } else if (imageType.equals(ImageType.OrchestrationElement.toString())) {
            LOGGER.info("ics.REST.deploy(): Creating orchestration image");
            type = ImageType.OrchestrationElement;
        } else {
            LOGGER.error("ics.REST.deploy(): Unknown image type '" + imageType + "', throwing an error");
            throw new WebApplicationException(Status.BAD_REQUEST);
        }

        try {
            ret = ics.createImage(type).getUuid().toString();
        } catch (Exception e) {
            throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
        }
        LOGGER.info("ics.REST.deploy(): Created image with id " + ret);

        return ret;
    }

    /**
     * Creates a new image based on the given input image requirement
     * @param input     image requirement (described in the XML format)
     * @return the newly-created image ID or 
     *          an exception error if it receives an invalid image type
     */
    private String createImageFromRequirement(String input) {
        String str = null;
        try {
            str = ics.createImage(input);
        } catch (Exception e) {
            LOGGER.debug("ics.REST.createImageFromRequirement(): image is null due to bad request");
            throw new WebApplicationException(Status.BAD_REQUEST);
        }

        LOGGER.info("ics.REST.createImageFromRequirement(): " + str);
        return str;
    }

    /**
     * Creates a new image from the given XML file.
     * For the image requirement, the XML format looks like below:
     * <pre>
     * <ImageTemplate>
     *     <operatingSystem>CentOS</operatingSystem>  <!-- for a wildcard usage, write: Linux -->
     *     <osVersion>5.8</osVersion>      
     *     <architecture>i386</architecture>    <!-- value only i386 or x86_64 -->
     *     <imageSize>10</imageSize>    <!-- in GB (integer only) -->
     * </ImageTemplate>
     * </pre>
     * Note that the above tags are all optional and can be omitted.
     * If this is the case, the ICS will randomly select the eligible base image.
     * 
     * @param inputStream
     * @param name
     * @return
     */
    @POST
    @Path("/request")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    public String deployWithInputRequirement(@FormDataParam("file") InputStream inputStream,
            @FormDataParam("name") String name) {

        //LOGGER.debug("ics.REST.deployWithInputRequirement(): Received XML input");
        //LOGGER.debug("ics.REST.deployWithInputRequirement(): Receiving multipart file upload for image " + imageId);
        LOGGER.debug("ics.REST.deployWithInputRequirement(): Received XML input: " + name);

        File xmlFile = null;
        String scDirectory = null;
        try {
            String uuid = UUID.randomUUID().toString();
            scDirectory = "/tmp/" + uuid + "_";
            xmlFile = new File(scDirectory + name);

            // Write the input stream to the given file
            FileOutputStream fos = new FileOutputStream(xmlFile);
            int read = 0;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                fos.write(bytes, 0, read);
            }
            inputStream.close();
            fos.close();
            LOGGER.debug("ics.REST.deployWithInputRequirement(): Written file to " + xmlFile.getAbsolutePath());
        } catch (IOException ioException) {
            throw new WebApplicationException(ioException, Status.INTERNAL_SERVER_ERROR);
        }

        String str = null;
        try {
            str = ics.createImage(xmlFile);
        } catch (Exception e) {
            LOGGER.debug("ics.REST.deployWithInputRequirement(): image is null");
            throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
        }

        xmlFile.delete(); // remove this temp file afterwards
        LOGGER.info("ics.REST.deployWithInputRequirement(): " + str);
        return str;
    }

    /**
     * Uploads the given file into the selected image ID 
     * @param imageId       image ID
     * @param inputStream   InputStream containing the file's content
     * @param name          filename
     */
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Path("/image/{id}/file")
    public void putFile(@PathParam("id") String imageId, @FormDataParam("file") InputStream inputStream,
            @FormDataParam("name") String name) {
        LOGGER.debug("ics.REST.putFile(): Receiving multipart file upload for image " + imageId);
        LOGGER.debug("ics.REST.putFile(): String name: " + name);
        // Save the uploaded file to a temporary file
        boolean bo = saveFileToSourceDirectory(imageId, inputStream, name, false, false);
        if (bo == false) {
            LOGGER.error("ics.REST.putFile(): Image " + imageId + " could be stored under " + sourceDirectory);
            return;
        }
    }

    /**
     * Uploads the given war file into the selected image ID 
     * @param imageId       image ID
     * @param inputStream   InputStream containing the file's content
     * @param name          the war filename
     */
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Path("/image/{id}/war")
    public void putWarFile(@PathParam("id") String imageId, @FormDataParam("file") InputStream inputStream,
            @FormDataParam("name") String name) {
        LOGGER.debug("ics.REST.putWarFile(): Receiving multipart file upload for image " + imageId);
        LOGGER.debug("ics.REST.putWarFile(): String file name: " + name);

        try {
            Image image = ics.getImage(imageId.trim());
            webappDirectory = image.getWebappsDirectoryName();
            LOGGER.debug("ics.REST.putWarFile(): webappDirectory = " + webappDirectory);
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.putWarFile(): IOException looking for image", ioException);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.error("ics.REST.putWarFile(): Image with id " + imageId + " not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        // Save uploaded content to a directory
        boolean bo = saveFileToSourceDirectory(imageId, inputStream, name, true, false);
        if (bo == false) {
            LOGGER.error("ics.REST.putWarFile(): Image " + imageId + " could be stored under " + sourceDirectory);
            return;
        }
    }

    /**
     * Uploads the given zip file into the selected image ID 
     * @param imageId       image ID
     * @param inputStream   InputStream containing the file's content
     * @param name          a zip filename
     */
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Path("/image/{id}/zip")
    public void putZipFile(@PathParam("id") String imageId, @FormDataParam("file") InputStream inputStream,
            @FormDataParam("name") String name) {
        LOGGER.debug("ics.REST.putZipFile(): Receiving multipart file upload for image " + imageId);
        LOGGER.debug("ics.REST.putZipFile(): String file name: " + name);

        // Save uploaded content to a temporary file
        //File tempFile = saveFileToTemporaryDirectory(inputStream, name);
        boolean bo = saveFileToSourceDirectory(imageId, inputStream, name, false, true);
        if (bo == false) {
            LOGGER.error("ics.REST.putZipFile(): Image " + imageId + " could be stored under " + sourceDirectory);
            return;
        }
    }

    /**
     * Saves the given input stream to a file in a given directory.
     * 
     * @param inputStream   InputStream containing the file's content
     * @param name          input filename
     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise.
     */
    private boolean saveFileToSourceDirectory(String imageId, InputStream inputStream, String name, boolean war,
            boolean zip) {
        File tempFile = null;
        File scDirectory = null;
        try {
            String completeSourceDirectory;
            if (war)
                completeSourceDirectory = sourceDirectory + "/" + imageId + "/" + webappDirectory;
            else
                completeSourceDirectory = sourceDirectory + "/" + imageId + "/" + targetDirectory;
            scDirectory = FileUtils.createDirectoryIfNotExists(completeSourceDirectory);
            tempFile = new File(scDirectory, name);

            // Write the input stream to the given file
            FileOutputStream fos = new FileOutputStream(tempFile);
            int read = 0;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                fos.write(bytes, 0, read);
            }
            inputStream.close();
            fos.close();
            LOGGER.debug("ics.REST.saveFileToSourceDirectory(): Written file to " + tempFile.getAbsolutePath());
        } catch (IOException ioException) {
            throw new WebApplicationException(ioException, Status.INTERNAL_SERVER_ERROR);
        }
        if (zip) {
            try {
                unzipFile(tempFile, tempFile.getParent());
                tempFile.delete();
            } catch (IOException io) {
                LOGGER.debug("ics.REST.saveFileToSourceDirectory(): failed by unzipping file");
                return false;
            }
        }
        // Return the temporary file that now contains the input stream
        return true;
    }

    /**
     * Sets the permission of a given file located on the image.
     * If it is a directory, it will change the permission recursively.
     * @param imageId       the image ID
     * @param filename      a filename. It can also be a directory name.
     * @param permissions   a permission mode, e.g. "644" or "600" 
     */
    @PUT
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Path("/image/{id}/permissions")
    public void setPermissions(@PathParam("id") String imageId, @FormDataParam("file") String filename,
            @FormDataParam("permissions") String permissions) {
        LOGGER.debug("ics.REST.setPermissions(): Receiving setPermissions for image " + imageId);
        LOGGER.debug("ics.REST.setPermissions(): File to change: " + filename);
        LOGGER.debug("ics.REST.setPermissions(): Permissions to set: " + permissions);

        try {
            ImageCreationService.getInstance().setPermissions(imageId, filename, permissions);
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.setPermissions(): I/O exception putting file to image " + imageId, ioException);
            throw new WebApplicationException(ioException, Response.Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.info("ics.REST.setPermissions(): No image with id " + imageId + " could be found");
            throw new WebApplicationException(Status.NOT_FOUND);
        } catch (UnsupportedStateException e) {
            LOGGER.error("ics.REST.setPermissions(): Wrong state for setting permissions on image", e);
            throw new WebApplicationException(Status.FORBIDDEN);
        }
    }

    /**
     * Finalizes the image with the given id.
     * 
     * @param imageId image ID
     * @return the URL of the image or an exception error message if image does not exist
     */
    @POST
    @Path("/image/{id}/finalize")
    public String finalize(@PathParam("id") String imageId) {
        try {
            ImageCreationService.getInstance().putFileDirectory(imageId, sourceDirectory);
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.finalize(): I/O exception putting file to image " + imageId + " -- "
                    + ioException.getMessage());
            throw new WebApplicationException(ioException, Response.Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.info("ics.REST.finalize(): No image with id " + imageId + " could be found");
            throw new WebApplicationException(Status.NOT_FOUND);
        } catch (UnsupportedStateException e) {
            LOGGER.error("ics.REST.finalize(): Wrong state for uploading file to image", e);
            throw new WebApplicationException(Status.FORBIDDEN);
        }

        LOGGER.debug("ics.REST.finalize(): Finalizing image " + imageId);
        try {
            Image finalizedImage = ics.finalizeImage(imageId);
            return finalizedImage.getUrl() + '\n';
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.finalize(): I/O exception finalizing image " + imageId + " -- "
                    + ioException.getMessage());
            throw new WebApplicationException(ioException, Response.Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException noImageException) {
            LOGGER.info("ics.REST.finalize(): No image with id " + imageId + " could be found");
            throw new WebApplicationException(Status.NOT_FOUND);
        } catch (StateChangeException e) {
            LOGGER.error("ics.REST.finalize(): Error changing state: " + e.getMessage());
            throw new WebApplicationException(Status.FORBIDDEN);
        }
    }

    /**
     * Deletes an image with the given ID
     * @param imageId  the image ID
     */
    @DELETE
    @Path("/image/{id}")
    public void deleteImage(@PathParam("id") String imageId) {
        LOGGER.debug("ics.REST.deleteImage(): Deleting image with id " + imageId);
        try {
            // Call the ImageCreationService's deleteImage() method
            String completeSourceDirectory = sourceDirectory + "/" + imageId;
            ics.deleteImage(imageId, completeSourceDirectory);

            LOGGER.debug("ics.REST.deleteImage(): Image " + imageId + " deleted successfuly");
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.deleteImage(): I/O exception deleting image", ioException);
            throw new WebApplicationException(ioException, Response.Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException noImageException) {
            LOGGER.debug("ics.REST.deleteImage(): Image with id " + imageId + " not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        }
    }

    /**
     * Deletes all the images from the database and in the physical hard disk.
     */
    @DELETE
    @Path("/image/flush")
    public void flushImages() {
        LOGGER.debug("ics.REST.flushImages(): Flusing ImageCreationService");
        ics.flushDatabase();
    }

    /**
     * Gets the status of an image
     * @param imageId   the image ID
     * @return the image status or an exception message if the image is not found
     */
    @GET
    @Path("/image/{id}/status")
    public String getImageStatus(@PathParam("id") String imageId) {
        LOGGER.debug("ics.REST.getImageStatus(): Returning status for image with id '" + imageId + "'");
        try {
            Image image = ics.getImage(imageId.trim());
            return image.getState().toString();
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.getImageStatus(): IOException looking for image", ioException);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException e) {
            LOGGER.error("ics.REST.getImageStatus(): Image with id " + imageId + " not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        }
    }

    /**
     * Changes the image status from FINALIZED to READY
     * @param imageId   the image ID
     */
    @POST
    @Path("/image/{id}/unfinalize")
    public void unfinalize(@PathParam("id") String imageId) {
        LOGGER.debug("ics.REST.unfinalize(): Unfinalizing image with id '" + imageId + "'");
        try {
            // Call the ImageCreationService's unfinalizeImage() method
            ics.unfinalizeImage(imageId);
        } catch (IOException ioException) {
            LOGGER.error("ics.REST.unfinalize(): I/O exception accessing image '" + imageId + "'");
            throw new WebApplicationException(ioException, Response.Status.INTERNAL_SERVER_ERROR);
        } catch (ImageNotFoundException noImageException) {
            LOGGER.error("ics.REST.unfinalize(): Image '" + imageId + "' not found");
            throw new WebApplicationException(Status.NOT_FOUND);
        } catch (StateChangeException e) {
            LOGGER.error("ics.REST.unfinalize(): LOGGER: " + e.getMessage());
            throw new WebApplicationException(Status.FORBIDDEN);
        }
    }

    /**
     * Uncompresses the given zip file. Unfortunately, the files' permission is not
     * preserved, especially with regards to an executable file.
     * @param zipFile       the zip file
     * @param destination   the directory location
     * @throws IOException  IO Exception
     */
    private void unzipFile(File zipFile, String destination) throws IOException {
        LOGGER.debug("ics.REST.unzipFile(): Unzipping " + zipFile + " to directory " + destination);
        //LOGGER.debug("ics.REST.unzipFile(): Opening input streams");
        FileInputStream fis = new FileInputStream(zipFile);
        ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis));
        ZipEntry entry;
        File destinationDirectory = new File(destination);

        while ((entry = zin.getNextEntry()) != null) {
            //LOGGER.debug("ics.REST.unzipFile(): Extracting: " + entry);

            if (entry.isDirectory()) {
                //LOGGER.debug("ics.REST.unzipFile(): Directory found, will be created");
                File targetDirectory = new File(destinationDirectory, entry.getName());
                targetDirectory.mkdir();
            } else {
                // extract data
                // open output streams
                int BUFFER = 2048;

                File destinationFile = new File(destinationDirectory, entry.getName());
                destinationFile.getParentFile().mkdirs();

                //LOGGER.debug("ics.REST.unzipFile(): Creating parent file of destination: "
                //        + destinationFile.getParent());
                //boolean parentDirectoriesCreated = destinationFile.getParentFile().mkdirs();                
                //LOGGER.debug("ics.REST.unzipFile(): Result of creating parents: "
                //        + parentDirectoriesCreated);

                FileOutputStream fos = new FileOutputStream(destinationFile);
                BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);

                int count;
                byte data[] = new byte[BUFFER];

                dest = new BufferedOutputStream(fos, BUFFER);
                while ((count = zin.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, count);
                }
                dest.flush();
                dest.close();
            }
        }

        LOGGER.debug("ics.REST.unzipFile(): Unzipping file is done");
        zin.close();
        fis.close();
    }

    /*******
    public static void main(String[] args) throws FileNotFoundException {
    FileInputStream is = new FileInputStream(new File("C:\\tmp\\ConvertPDFtoText.zip"));
    ImageCreationServiceREST rest = new ImageCreationServiceREST();
    rest.putZipFile("001", is, "THtest.zip");
    }
    ********/

}