CompressorUtilities.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.openspcoop2.utils.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.CompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.resources.FileSystemUtilities;

/**
 * CompressorUtilities
 *
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class CompressorUtilities {

	public static void main(String [] args) throws Exception{
		
		testCompressor(null);
		testArchive(null);
		
	}
	
	private static void print(String msg) {
		System.out.println(msg);
	}
	
	private static final String COMPRESSO_DIMENSIONE = "Compresso, dimensione: ";
	private static final String COMPRESSO_IN_STRING = "Compresso, in stringa: ";
	private static final String DECOMPRESSO_IN_STRING = "De-Compresso, in stringa: ";
	private static final String INFORMAZIONE_DECOMPRESSA_DIFFERENTE = "Informazione decompressa non uguale al sorgente";
	private static final String INFORMAZIONE_DECOMPRESSA_DIFFERENTE_SIZE = "Informazione decompressa non uguale al sorgente (size)";
	private static final String INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1 = "Informazione decompressa non uguale al sorgente (entry1 name:";
	private static final String INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_2 = "Informazione decompressa non uguale al sorgente (entry2 name:";
	private static final String INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE = "Informazione decompressa non uguale al sorgente (entry1 contenuto differente)";
	private static final String INFORMAZIONE_DECOMPRESSA_UGUALE = "De-Compresso: test di comparazione completati con successo";
	private static final String DECOMPRESSO = "De-Compresso: ";
	private static final String ATTESO = " atteso:";
	
	public static void testCompressor(CompressorType tipo) throws UtilsException{
		
		String test = "<prova xmlns=\"www.test.it\">PROVA</prova>";
		byte[]testB = test.getBytes();
		
		if(tipo==null || CompressorType.DEFLATER.equals(tipo)) {
			print("\n\n=== DEFLATER ===");
			byte [] compress = compress(testB, CompressorType.DEFLATER);
			print(COMPRESSO_DIMENSIONE+compress.length);
			print(COMPRESSO_IN_STRING+new String(compress));
			String decompresso = new String(decompress(compress, CompressorType.DEFLATER));
			print(DECOMPRESSO_IN_STRING+decompresso);
			if(!decompresso.equals(test)) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE);
			}
		}
		
		if(tipo==null || CompressorType.GZIP.equals(tipo)) {
			print("\n\n=== GZIP ===");
			byte [] compress = compress(testB, CompressorType.GZIP);
			print(COMPRESSO_DIMENSIONE+compress.length);
			print(COMPRESSO_IN_STRING+new String(compress));
			String decompresso = new String(decompress(compress, CompressorType.GZIP));
			print(DECOMPRESSO_IN_STRING+decompresso);
			if(!decompresso.equals(test)) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE);
			}
		}
			
		if(tipo==null || CompressorType.ZIP.equals(tipo)) {
			print("\n\n=== ZIP ===");
			byte [] compress = compress(testB, CompressorType.ZIP);
			print(COMPRESSO_DIMENSIONE+compress.length);
			print(COMPRESSO_IN_STRING+new String(compress));
			String decompresso = new String(decompress(compress, CompressorType.ZIP));
			print(DECOMPRESSO_IN_STRING+decompresso);
			if(!decompresso.equals(test)) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE);
			}
		}
		
	}
	
	public static void testArchive(ArchiveType tipo) throws Exception{
		
		String entryName = "entry1";
		String test = "<prova xmlns=\"www.test.it\">PROVA</prova>";
		byte[]testB = test.getBytes();
		
		String entryName2 = "dir/subdir/entry2";
		String test2 = "<prova xmlns=\"www.test.it\">PROVA2</prova>";
		byte[]test2B = test2.getBytes();
		
		List<Entry> entries = new ArrayList<>();
		entries.add(new Entry(entryName,testB));
		entries.add(new Entry(entryName2,test2B));
		
		if(tipo==null || ArchiveType.ZIP.equals(tipo)) {
			print("\n\n=== ZIP ===");
			byte [] compress = archive(entries, ArchiveType.ZIP);
			print(COMPRESSO_DIMENSIONE+compress.length);
			List<Entry> entriesRead = read(compress, ArchiveType.ZIP);
			print(DECOMPRESSO+entriesRead.size());
			if(entriesRead.size()!=2) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_SIZE);
			}
			Entry entryRead1 = entriesRead.get(0);
			Entry entryRead2 = entriesRead.get(1);
			if(!entryName.equals(entryRead1.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1+entryRead1.getName()+ATTESO+entryName+")");
			}
			if(!entryName2.equals(entryRead2.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_2+entryRead2.getName()+ATTESO+entryName2+")");
			}
			if(!test.equals(new String(entryRead1.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			if(!test2.equals(new String(entryRead2.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			print(INFORMAZIONE_DECOMPRESSA_UGUALE);
		}
		
		if(tipo==null || ArchiveType.TAR.equals(tipo)) {
			print("\n\n=== TAR ===");
			byte [] compress = archive(entries, ArchiveType.TAR);
			print(COMPRESSO_DIMENSIONE+compress.length);
			List<Entry> entriesRead = read(compress, ArchiveType.TAR);
			print(DECOMPRESSO+entriesRead.size());
			if(entriesRead.size()!=2) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_SIZE);
			}
			Entry entryRead1 = entriesRead.get(0);
			Entry entryRead2 = entriesRead.get(1);
			if(!entryName.equals(entryRead1.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1+entryRead1.getName()+ATTESO+entryName+")");
			}
			if(!entryName2.equals(entryRead2.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_2+entryRead2.getName()+ATTESO+entryName2+")");
			}
			if(!test.equals(new String(entryRead1.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			if(!test2.equals(new String(entryRead2.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			print(INFORMAZIONE_DECOMPRESSA_UGUALE);
		}
		
		if(tipo==null || ArchiveType.TGZ.equals(tipo)) {
			print("\n\n=== TGZ ===");
			byte [] compress = archive(entries, ArchiveType.TGZ);
			print(COMPRESSO_DIMENSIONE+compress.length);
			List<Entry> entriesRead = read(compress, ArchiveType.TGZ);
			print(DECOMPRESSO+entriesRead.size());
			if(entriesRead.size()!=2) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_SIZE);
			}
			Entry entryRead1 = entriesRead.get(0);
			Entry entryRead2 = entriesRead.get(1);
			if(!entryName.equals(entryRead1.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1+entryRead1.getName()+ATTESO+entryName+")");
			}
			if(!entryName2.equals(entryRead2.getName())) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_2+entryRead2.getName()+ATTESO+entryName2+")");
			}
			if(!test.equals(new String(entryRead1.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			if(!test2.equals(new String(entryRead2.getContent()))) {
				throw new UtilsException(INFORMAZIONE_DECOMPRESSA_DIFFERENTE_ENTRY_1_DIFFERENTE);
			}
			print(INFORMAZIONE_DECOMPRESSA_UGUALE);
		}

		
	}
	
    public static byte[] compress(byte[] content, CompressorType type) throws UtilsException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            OutputStream out = null;
            switch (type) {
			case DEFLATER:
				out = new DeflaterOutputStream(baos);
				out.write(content);
				break;
			case GZIP:
				out = new GZIPOutputStream(baos);
				out.write(content);
				break;
			case ZIP:
				out = new ZipOutputStream(baos);
				((ZipOutputStream)out).putNextEntry(new ZipEntry("dat"));
				out.write(content);
				((ZipOutputStream)out).closeEntry();
				break;
			}
            if(out==null) {
            	throw new UtilsException("OutputStream undefined");
            }
            out.flush();
            out.close();
            baos.flush();
            baos.close();
            return baos.toByteArray();
        } catch (Exception e) {
            throw new UtilsException(e.getMessage(),e);
        }
    }
    
    public static byte[] archive(List<Entry> entries, ArchiveType type) throws UtilsException{
		try {
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			
			org.apache.commons.compress.archivers.ArchiveOutputStream<?>  out = null;
			org.apache.commons.compress.compressors.CompressorOutputStream compressorOut = null;
            switch (type) {
            case TAR:
				out = new TarArchiveOutputStream(bout);
				break;
			case TGZ:
				compressorOut = new GzipCompressorOutputStream(bout);
				out = new TarArchiveOutputStream(compressorOut);
				break;
			case ZIP:
				out = new ZipArchiveOutputStream(bout);
				break;
			}
            if(out==null) {
            	throw new UtilsException("OutputStream undefined");
            }
			for (Entry entry : entries) {
				String name = entry.getName();
				ArchiveEntry archiveEntry = null;
				switch (type) {
	            case TAR:
				case TGZ:
					archiveEntry = new TarArchiveEntry(name);
					((TarArchiveEntry)archiveEntry).setSize(entry.getContent().length); 
					break;
				case ZIP:
					archiveEntry = new ZipArchiveEntry(name);
					break;
				}
	            switch (type) {
	            case TAR:
				case TGZ:
	            	((TarArchiveOutputStream)out).putArchiveEntry((TarArchiveEntry) archiveEntry);
					break;
				case ZIP:
					((ZipArchiveOutputStream)out).putArchiveEntry((ZipArchiveEntry)archiveEntry);
					break;
				}
	            out.write(entry.getContent());
				out.closeArchiveEntry();
			}
			out.flush();
			out.close();
			if(compressorOut!=null) {
				compressorOut.flush();
				compressorOut.close();
			}
			bout.flush();
			bout.close();
			return bout.toByteArray();
		}catch(Exception e){
			throw new UtilsException("Errore durante la scrittura '"+type+"': "+e.getMessage(),e);
		}
	}

    public static byte [] decompress(byte[] bytes, CompressorType type) throws UtilsException {
        InputStream in = null;
        ByteArrayInputStream bin = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
        	bin = new ByteArrayInputStream(bytes);
        	byte[] buffer = new byte[8192];
            int len;
        	switch (type) {
 			case DEFLATER:
 				in = new InflaterInputStream(bin);
 	            while((len = in.read(buffer))>0)
 	                baos.write(buffer, 0, len);
 				break;
 			case GZIP:
 				in = new GZIPInputStream(bin);
 	            while((len = in.read(buffer))>0)
 	                baos.write(buffer, 0, len);
 				break;
 			case ZIP:
 				File f = FileSystemUtilities.createTempFile("unzip", "zip");
 				ZipFile zf = null;
 				try{
 					FileSystemUtilities.writeFile(f, bytes);
 					zf = new ZipFile(f);
 					ZipEntry ze = zf.entries().nextElement();
 					in = zf.getInputStream(ze);
 	 	            while((len = in.read(buffer))>0)
 	 	                baos.write(buffer, 0, len);
 				}finally{
 					try{
 						if(f!=null) {
 							java.nio.file.Files.delete(f.toPath());
 						}
 					}catch(Exception eClose){
 						// ignore
 					}
 					try{
 						if(zf!=null){
 							zf.close();
 						}
 					}catch(Exception eClose){
 						// close
 					}
 				}
 				break;
 			}
            baos.flush();
            baos.close();
            return baos.toByteArray();
        }  catch (Exception e) {
            throw new UtilsException(e.getMessage(),e);
        }
    }
	
    public static List<Entry> read(byte[] archiveContent, ArchiveType type) throws UtilsException{
		try {
			List<Entry> list = new ArrayList<>();
			
			ByteArrayInputStream bin = new ByteArrayInputStream(archiveContent);
			ArchiveInputStream<?> in = null;
			CompressorInputStream compressorIn = null;
			switch (type) {
			case TAR:
				in = new TarArchiveInputStream(bin);
				break;
			case TGZ:
				compressorIn = new GzipCompressorInputStream(bin);
				in = new TarArchiveInputStream(compressorIn);
				break;
			case ZIP:
				in = new ZipArchiveInputStream(bin);
				break;
			}
            if(in==null) {
            	throw new UtilsException("InputStream undefined");
            }
			ArchiveEntry entry;
		    while ((entry = in.getNextEntry()) != null) {
		        String name = entry.getName();
		    	/**print(name);*/
		        
		        if(name!=null && !( name.endsWith("/") || name.endsWith("\\") ) ){
		        	Entry zentry = new Entry();
	        		zentry.setName(name);
	        		
	        		ByteArrayOutputStream bout = new ByteArrayOutputStream();
	        		byte [] contents = new byte[4096];
	        		int direct;
	        		while ((direct = in.read(contents, 0, contents.length)) >= 0) {
	        			/**print("Read " + direct + "bytes content.");*/
	        			bout.write(contents, 0, direct);
	        		}
	        		bout.flush();
	        		bout.close();
	        		
	        		zentry.setContent(bout.toByteArray());
	        		list.add(zentry);
		        }
		        
		       /** in.closeEntry(); */
		    }
		    if(compressorIn!=null) {
		    	compressorIn.close();
		    }
		    in.close();
		    bin.close();

	        return list;
	        
		}catch(Exception e){
			throw new UtilsException("Errore durante la lettura '"+type+"': "+e.getMessage(),e);
		}
	}
}