gov.ca.cwds.rest.util.jni.CmsPKCompressor.java Source code

Java tutorial

Introduction

Here is the source code for gov.ca.cwds.rest.util.jni.CmsPKCompressor.java

Source

package gov.ca.cwds.rest.util.jni;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.bind.DatatypeConverter;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pkware.deflate.DeflateOutputStream;
import com.pkware.deflate.InflateInputStream;

import gov.ca.cwds.rest.services.ServiceException;

/**
 * Compresses (deflates) and decompresses (inflates) PK archives created by the Windows PKWare
 * library.
 * 
 * <p>
 * <strong>NOTE: </strong>This class only works with PK-compressed docs, not the LZW variable 15-bit
 * algorithm. For the latter, see {@link LZWEncoder}.
 * </p>
 *
 * <p>
 * This class provides specialized methods to compress/decompress documents for various inputs,
 * including files, streams, byte arrays, base64-encoded strings, and hexadecimal strings.
 * </p>
 *
 * <p>
 * For some unknown reason, this particular compression algorithm (PK_COMPRESS_METHOD_DEFLATE, level
 * 6) <strong>does not require a current license key</strong>. For example, this initialization is
 * not needed:
 * </p>
 * 
 * <pre>
 * <code>PKSession session = new PKSession(PKWARE_LICENSE_KEY);</code>
 * </pre>
 * 
 * <strong>Java PKWare SDK details:</strong>
 * <ul>
 * <li>Name: com/pkware/archive</li>
 * <li>Implementation-Vendor: PKWARE, Inc.</li>
 * <li>Specification-Title: SecureZIP Toolkit for Java</li>
 * <li>Implementation-Title: com.pkware.archive</li>
 * <li>Implementation-Version: 3.20</li>
 * <li>Specification-Version: 3.20</li>
 * <li>Specification-Vendor: PKWARE, Inc.</li>
 * </ul>
 * 
 * @author CWDS API Team
 * @see LZWEncoder
 */
public class CmsPKCompressor {

    private static final Logger LOGGER = LoggerFactory.getLogger(CmsPKCompressor.class);

    private static final int DEFAULT_COMPRESSION_LEVEL = 6;
    private static final int DEFAULT_OUTPUT_SIZE = 0x10000;

    /**
     * Constructor
     */
    public CmsPKCompressor() {
        // Default, no-op. Get over it, SonarQube.
    }

    /**
     * Decompress (inflate) a CMS PKWare archive.
     * 
     * @param inputFileName file name of resulting decompressed output
     * @param outputFileName file name to decompress
     * @throws IOException If an I/O error occurs
     */
    public void decompressFile(String inputFileName, String outputFileName) throws IOException {
        if (StringUtils.isBlank(inputFileName) || StringUtils.isBlank(outputFileName)) {
            throw new IOException("REQUIRED: file names cannot be null");
        }

        try (final FileInputStream fis = new FileInputStream(createFile(inputFileName));
                final InputStream iis = new InflateInputStream(fis, true);
                final FileOutputStream fos = new FileOutputStream(createFile(outputFileName));) {
            IOUtils.copy(iis, fos);
        } catch (Exception e) {
            throw new ServiceException("Error copying file", e);
        }
    }

    /**
     * Decompress (inflate) raw bytes of a PK-compressed document.
     * 
     * @param bytes raw bytes of PK-compressed document.
     * @return byte array of decompressed document
     * @throws IOException If an I/O error occurs
     */
    public byte[] decompressBytes(byte[] bytes) throws IOException {
        if (bytes == null) {
            throw new IOException("REQUIRED: bytes to decompress cannot be null");
        }

        final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        final InputStream iis = new InflateInputStream(bis, true);
        final ByteArrayOutputStream bos = new ByteArrayOutputStream(DEFAULT_OUTPUT_SIZE);

        IOUtils.copy(iis, bos);
        iis.close();
        bis.close();
        bos.flush();
        bos.close();

        final byte[] retval = bos.toByteArray();
        LOGGER.debug("CmsPKCompressor.decompress(byte[]): retval len={}", retval.length);
        return retval;
    }

    /**
     * Decompress (inflate) an InputStream of a PK-compressed document.
     * 
     * @param input InputStream of PK-compressed document.
     * @return byte array of decompressed document
     * @throws IOException If an I/O error occurs
     */
    public byte[] decompressStream(InputStream input) throws IOException {
        if (input == null) {
            throw new IOException("REQUIRED: input stream to decompress cannot be null");
        }

        final InputStream iis = new InflateInputStream(input, true);
        final ByteArrayOutputStream bos = new ByteArrayOutputStream(DEFAULT_OUTPUT_SIZE);
        IOUtils.copy(iis, bos);

        iis.close();
        bos.flush();
        bos.close();
        return bos.toByteArray();
    }

    /**
     * Convenience method. Decompress (inflate) a base64-encoded string of a PK-compressed archive.
     * 
     * @param base64Doc base64-encoded, PK-compressed archive
     * @return raw byte array of decompressed document
     * @throws IOException If an I/O error occurs
     */
    public byte[] decompressBase64(String base64Doc) throws IOException {
        if (base64Doc == null) {
            throw new IOException("REQUIRED: base64 string to decompress cannot be null");
        }
        final byte[] bytes = decompressBytes(DatatypeConverter.parseBase64Binary(base64Doc.trim()));
        LOGGER.debug("CmsPKCompressor.decompressBase64(String): bytes len={}", bytes.length);
        return bytes;
    }

    /**
     * Convenience method. Decompress (inflate) a hexadecimal string of a PK-compressed archive.
     * 
     * @param hex base64-encoded, PK-compressed archive
     * @return raw byte array of decompressed document
     * @throws IOException If an I/O error occurs
     */
    public byte[] decompressHex(String hex) throws IOException {
        if (StringUtils.isBlank(hex)) {
            throw new IOException("REQUIRED: hex to decompress cannot be null");
        }

        final byte[] bytes = decompressBytes(DatatypeConverter.parseHexBinary(hex.trim()));
        LOGGER.debug("CmsPKCompressor.decompressHex(String): bytes len={}", bytes.length);
        return bytes;
    }

    /**
     * Compress (deflate) a CMS PKWare archive and writes resulting compressed document to given
     * output file.
     * 
     * @param inputFileName file name to decompress
     * @param outputFileName file name of resulting decompressed output
     * @throws IOException If an I/O error occurs
     */
    public void compressFile(String inputFileName, String outputFileName) throws IOException {
        if (StringUtils.isBlank(inputFileName) || StringUtils.isBlank(outputFileName)) {
            throw new IOException("REQUIRED: file names cannot be null");
        }

        try (final FileInputStream fis = new FileInputStream(createFile(inputFileName));
                final OutputStream fos = new DeflateOutputStream(new FileOutputStream(createFile(outputFileName)),
                        DEFAULT_COMPRESSION_LEVEL, true);) {
            IOUtils.copy(fis, fos);
        } catch (Exception e) {
            throw new ServiceException("Unable to copy file", e);
        }
    }

    public String compressBase64ToHex(String base64) throws IOException {
        return DatatypeConverter.printHexBinary(compressBytes(DatatypeConverter.parseBase64Binary(base64)))
                .toLowerCase();
    }

    /**
     * Convenience method. Compress (deflate) a document InputStream.
     * 
     * @param bytes raw bytes of the document to compress
     * @return raw byte array of compressed document
     * @throws IOException If an I/O error occurs
     */
    public byte[] compressBytes(byte[] bytes) throws IOException {
        if (bytes == null) {
            throw new IOException("REQUIRED: bytes to compress cannot be null");
        }

        final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        final ByteArrayOutputStream bos = new ByteArrayOutputStream(DEFAULT_OUTPUT_SIZE);
        final OutputStream dos = new DeflateOutputStream(bos, DEFAULT_COMPRESSION_LEVEL, true);

        IOUtils.copy(bis, dos);
        bis.close();
        dos.close();

        return bos.toByteArray();
    }

    private File createFile(String file) {
        return new File(FilenameUtils.getFullPath(file), FilenameUtils.getName(file)); // NOSONAR
    }

}