Saturday, August 5, 2017

Spring Boot Uygulamalarının SSL Yapılandırması

Spring Boot uygulamalarında yapılandırma dosyası varsayılan olarak application.properties dosyasıdır. Spring Boot uygulamalarının SSL yapılandırmasını da application.properties dosyasından gerçekleştireceğiz. Sertifika otaritesinden aldığımız sertifikaları PKCS12 formatında Anahtar Deposu  (=KeyStore) dosyasında saklıyoruz. PKCS12kapalı anahtar ve sertifikaları depolamak ve uygulamalar tarafından erişilebilmesini sağlamak amacıyla geliştirlen dilden ve platformdan bağımsız bir saklama kabı standardıdır. PKCS12 dosyasını oluşturmak amacıyla aşağıdaki komutu çalıştırmanız gerekir:

openssl pkcs12 -export -inkey example.com.key -in example.com.cer -name mywebapp -out /tmp/keystore.p12 -password

Bu komut, parametre olarak kapalı anahtar (example.com.key) ve sertifikayı (example.com.cer) alıyor ve PKCS12 standardına uygun olarak /tmp/keystore.p12 isimli dosyada saklıyor.  Komutu çalıştırdığımızda bizden bir parola isteyecektir. Parola olarak Secret_1234 karakter katarını girmiş olalım. Bu parola ile uygulamalar Anahtar Deposundaki anahtar ya da sertifikayı okuyabilirler. Anahtar  Deposunda çok sayıda anahtar ve sertifika saklamak mümkündür. Bu nedenle Anahtar ve sertifikayı saklarken bir etiket atıyoruz. Örneğimizde, Anahtar Deposunda sakladığımız kapalı anahtar ve sertifikaya mywebapp etiketini verdik. Sonuç olarak, uygulamalar, Anahtar Deposunda sakladığımız anahtarı ve sertifikayı mywebapp etiketi ve Secret_1234 parolası ile okuyabilirler.

Artık Spring Boot uygulamasında SSL yapılandırması için application.properties dosyası içerisine gerekli tanımlamaları yapabiliriz:

server.ssl.key-store=/tmp/keystore.p12
server.ssl.key-store-password=Secret_1234
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=mywebapp

Spring Boot uygulamalarından bağımsız olarak Java uygulamalarının sertifika dosyasını okuyabilmeleri için Java Sanal Makinasına aşağıdaki parametreleri geçirmek gerekir:

export JAVA_OPTS="-Dserver.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.keyStoreType=pkcs12"

Wednesday, August 2, 2017

Java'da Dizin Arşivleme, Sıkıştırma ve Açma İşlemleri


Java 7 ile birlikte üçüncü parti bir kütüphane kullanmadan ZIP API yardımı ile arşivleme, sıkıştırma ve sıkıştırılmış dosyayı geri açma işlemlerini kolaylıkla gerçekleştirebiliyoruz. ZIP API, NIO.2 ve Stream API ile yakın işbirliği olan bir API. ZIP API, bize Java 7'de ve Java 8'de gelen yenilikleri kullandırıyor. Bu yazıda ZIP API'nin yeteneklerini örnek problemler üzerinden inceleyeceğiz.

1. Java Nesnelerini Sıkıştırılmış Zip Dosyası Olarak Saklamak

Bir dizi Customer sınıfı nesnesini zip dosyası içinde customers.dat kaydıyla saklamak istiyoruz:

Customer.java:

package com.example.zip;

import java.io.Serializable;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Customer implements Serializable {
   private String identityNo;
   private String firstName;
   private String lastName;

        public Customer(String identityNo, String firstName, String lastName) {
            this.identityNo = identityNo;
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getIdentityNo() {
            return identityNo;
        }

        public void setIdentityNo(String identityNo) {
            this.identityNo = identityNo;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

    @Override
    public String toString() {
        return "Customer{" + "identityNo=" + identityNo + ", firstName=" + firstName + ", lastName=" + lastName + '}';
    }

}

CompressJavaObjects.java:

package com.example.zip;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CompressJavaObjects {
    public static void main(String[] args) throws Exception {
        final File zipFile = new File("c:/tmp","customers.zip");
        List<Customer> customers= List.of(
                new Customer("1", "jack", "shephard"),
                new Customer("2", "kate", "austen"),
                new Customer("3", "james", "sawyer"),
                new Customer("4", "ben", "linus"),
                new Customer("5", "jin", "kwon")
        );
        try(
            final FileOutputStream fos = new FileOutputStream(zipFile);
            final ZipOutputStream zos= new ZipOutputStream(fos);                
            final BufferedOutputStream bos= new BufferedOutputStream(zos);
            final ObjectOutputStream oos= new ObjectOutputStream(bos);
        ){
            final ZipEntry zipEntry = new ZipEntry("customers.dat");
            zos.putNextEntry(zipEntry);
            oos.writeObject(customers);
        }
    }
}

2. Java Nesnelerini Sıkıştırılmış Dosyadan Geri Kazanmak

Bir dizi Customer sınıfı nesnesini zip dosyası içindeki customers.dat kaydından geri almak istiyoruz:

package com.example.zip;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class DecompressJavaObjects {
    public static void main(String[] args) throws Exception {
        final Path path = Paths.get("c:/tmp", "customers.zip");
        final File file = path.toFile();
        ZipFile zipFile= new ZipFile(file);
        try(
            final FileInputStream fis = new FileInputStream(file);
            final ZipInputStream zis= new ZipInputStream(fis);                
        ){
            final ZipEntry zipEntry = zis.getNextEntry();
            final BufferedInputStream bis= new BufferedInputStream(zipFile.getInputStream(zipEntry));
            final ObjectInputStream ois= new ObjectInputStream(bis);
            List<Customer> customers= (List<Customer>) ois.readObject();
            ois.close();
            bis.close();
        }
    }
}

3. Zip Dosya İçindeki Dizinleri Sözlük Sırasına Göre Sıralı Olarak Listelemek

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ListDirectoriesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());        
        List<String> directories= zipFile.stream()
                .filter(ZipEntry::isDirectory)
                .map(ZipEntry::getName)
                .map(path -> path.substring(0, path.length()-1))
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        directories.forEach(System.out::println);
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

mastermind-game
mastermind-game/.settings
mastermind-game/src
mastermind-game/src/main
mastermind-game/src/main/java
mastermind-game/src/main/java/com
mastermind-game/src/main/java/com/example
mastermind-game/src/main/java/com/example/service
mastermind-game/src/main/java/com/example/service/impl
mastermind-game/src/main/java/com/example/validation
mastermind-game/src/main/java/com/example/web
mastermind-game/src/main/java/com/example/web/controller
mastermind-game/src/main/java/com/example/web/model
mastermind-game/src/main/java/com/example/web/viewmodel
mastermind-game/src/main/resources
mastermind-game/src/main/webapp
mastermind-game/src/main/webapp/WEB-INF
mastermind-game/src/main/webapp/WEB-INF/pages
mastermind-game/src/main/webapp/WEB-INF/tags
mastermind-game/src/main/webapp/resources
mastermind-game/src/main/webapp/resources/css
mastermind-game/src/main/webapp/resources/images

4. Zip Dosya İçindeki Dizin ve Dosyaları Saymak

Zip dosya içindeki toplam dizin ve sıradan dosya sayısını bulmak istiyoruz. Bunu yaparken ayrıca her bir dizindeki dosya sayısını da elde etmek istiyoruz.

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CountFilesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());
        
        final Function<ZipEntry, String> groupByParentDirectory = entry -> entry.getName().substring(0, entry.getName().lastIndexOf('/'));
        final Predicate<ZipEntry> isDirectory = ZipEntry::isDirectory;
        final Predicate<ZipEntry> isFile = isDirectory.negate();
        
        Map<String,Long> fileCounts= 
            zipFile.stream()
                   .filter(isFile)
                   .collect(Collectors.groupingBy(groupByParentDirectory,Collectors.counting()));
        fileCounts.entrySet().forEach( 
            pair -> System.out.println(
                String.format("There are %d files in the directory %s",pair.getValue(),pair.getKey())
            )
        );
                  
        long numberOfFiles= fileCounts.values().stream().mapToLong(Long::longValue).sum();
        System.out.println("Total number of files: "+numberOfFiles);
        System.out.println("Total number of directories: "+fileCounts.size());
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

There are 10 files in the directory mastermind-game/.settings
There are 2 files in the directory mastermind-game/src/main/webapp/resources/images
There are 4 files in the directory mastermind-game/src/main/java/com/example/web/model
There are 2 files in the directory mastermind-game/src/main/webapp/resources/css
There are 4 files in the directory mastermind-game
There are 1 files in the directory mastermind-game/src/main/java/com/example/service/impl
There are 1 files in the directory mastermind-game/src/main/java/com/example/service
There are 4 files in the directory mastermind-game/src/main/webapp/WEB-INF
There are 2 files in the directory mastermind-game/src/main/java/com/example/web/controller
There are 5 files in the directory mastermind-game/src/main/webapp/WEB-INF/pages
There are 5 files in the directory mastermind-game/src/main/resources
There are 1 files in the directory mastermind-game/src/main/java/com/example/web/viewmodel
There are 6 files in the directory mastermind-game/src/main/java/com/example/validation
There are 7 files in the directory mastermind-game/src/main/webapp/WEB-INF/tags
Total number of files: 54
Total number of directories: 14

5. Zip Dosya İçindeki java Uzantılı Dosyaları Listelemek

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ListJavaFilesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());
        final Predicate<ZipEntry> isDirectory = ZipEntry::isDirectory;        
        final Predicate<ZipEntry> isFile = isDirectory.negate();
        final Comparator<ZipEntry> sortByName = (z1,z2) -> Paths.get(z1.getName()).toFile().getName().compareTo(Paths.get(z2.getName()).toFile().getName());
        Predicate<ZipEntry> isJavaFile= entry -> entry.getName().endsWith(".java");
        List<ZipEntry> javaFiles= 
                zipFile.stream()
                       .filter(isFile.and(isJavaFile))
                       .sorted(sortByName)
                       .collect(Collectors.toList());
        javaFiles.forEach(System.out::println);
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

mastermind-game/src/main/java/com/example/service/AuthenticationService.java
mastermind-game/src/main/java/com/example/validation/Email.java
mastermind-game/src/main/java/com/example/web/model/Game.java
mastermind-game/src/main/java/com/example/web/controller/GameController.java
mastermind-game/src/main/java/com/example/web/model/GameStatus.java
mastermind-game/src/main/java/com/example/validation/Iban.java
mastermind-game/src/main/java/com/example/validation/IbanValidator.java
mastermind-game/src/main/java/com/example/web/controller/LogonController.java
mastermind-game/src/main/java/com/example/web/viewmodel/LogonViewModel.java
mastermind-game/src/main/java/com/example/web/model/Move.java
mastermind-game/src/main/java/com/example/web/model/Player.java
mastermind-game/src/main/java/com/example/service/impl/SimpleAuthenticationService.java
mastermind-game/src/main/java/com/example/validation/StrongPassword.java
mastermind-game/src/main/java/com/example/validation/TcKimlikNo.java
mastermind-game/src/main/java/com/example/validation/TcKimlikNoValidator.java

6. Bir Dizini Özyinelemeli (=Recursive) Olarak Zip Formatında Sıkıştırmak


c:/tmp/figures dizini altındaki, ne kadar derinlikte olursa olsun tüm dosya ve dizinleri Zip dosyasında arşivlemek ve sıkıştırılmış olarak saklamak istiyoruz:

package com.example.zip;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CreateZipFileRecursivelyFromDirectory {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path directory = Paths.get("c:/tmp/figures");
        final File zipFile = new File("c:/tmp","figures.zip");
        try(
            final FileOutputStream fos = new FileOutputStream(zipFile);
            final ZipOutputStream zos= new ZipOutputStream(fos);                                
        ) {
            Files.walkFileTree(directory, new ZipperVisitor(zos));
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }        
    }
}

class ZipperVisitor implements FileVisitor<Path>{
    final ZipOutputStream zos;                

    public ZipperVisitor(ZipOutputStream zos) {
        this.zos = zos;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        ZipEntry zipEntry= new ZipEntry(file.toFile().getPath());
        zos.putNextEntry(zipEntry);
        if (file.toFile().isFile()){
            Files.copy(file, zos);        
        }            
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;        
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;
    }
}

7. Zip Dosyasını Bir Dizine Açmak

package com.example.zip;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ExtractZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "figures.zip").toFile());
        Consumer<ZipEntry> extractFile= zipEntry -> {
            File file= new File(zipEntry.getName());
            if (zipEntry.isDirectory()){
                file.mkdirs();
            } else {
                try {
                    Files.copy(zipFile.getInputStream(zipEntry), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
                } catch (IOException ex) {
                    Logger.getLogger(ExtractZipFile.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        };
        zipFile.stream().forEach(extractFile); 
    }
}

8. Zip dosyası Üzerinde Değişiklik Yapmak

Mevcut bir Zip dosyası üzerinde dosya silmek, dosya eklemek ve dosya içeriğini güncellemek gibi değişiklikler yapmak istiyoruz:

package com.example.zip;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ModifyZipFile {

    public static void main(String[] args) throws IOException {
        final File originalFile = new File("c:/tmp", "figures.zip");
        final File tempFile = new File("c:/tmp", "figures-temp.zip");
        final List<String> filesToAdd = List.of("c:\\tmp\\figures\\jquery-fig1.png", "c:\\tmp\\figures\\jquery-fig2.png", "c:\\tmp\\figures\\jquery-fig3.png");
        final List<String> filesToDelete = List.of("c:\\tmp\\figures\\jboss-scanner1.png", "c:\\tmp\\figures\\jboss-scanner2.png", "c:\\tmp\\figures\\jboss-scanner3.png");
        final List<String> filesToUpdate = List.of("c:\\tmp\\figures\\mongo-1.png", "c:\\tmp\\figures\\mongo-2.png", "c:\\tmp\\figures/mongo-3.png");
        final ZipFile originalZipFile = new ZipFile(originalFile);
        final FileOutputStream tempFileOutputStream = new FileOutputStream(tempFile);
        final ZipOutputStream zos = new ZipOutputStream(tempFileOutputStream);
        final Predicate<ZipEntry> deleted = zipEntry -> filesToDelete.contains(zipEntry.getName());
        final Predicate<ZipEntry> notDeleted = deleted.negate();
        final Predicate<ZipEntry> updated = zipEntry -> filesToUpdate.contains(zipEntry.getName());
        final Predicate<ZipEntry> notUpdated = updated.negate();
        final Consumer<ZipEntry> copyToTempZipFile = zipEntry -> {
            try {
                ZipEntry newZipEntry = new ZipEntry(zipEntry);
                zos.putNextEntry(zipEntry);
                originalZipFile.getInputStream(zipEntry).transferTo(zos);
            } catch (IOException ex) {
                Logger.getLogger(CreateZipFileFromDirectory.class.getName()).log(Level.SEVERE, null, ex);
            }
        };
        originalZipFile.stream()
                .filter(notDeleted.and(notUpdated))
                .forEach(copyToTempZipFile);
        final Consumer<String> addToZipFile = file -> {
            try {
                ZipEntry zipEntry= new ZipEntry(file);
                zos.putNextEntry(zipEntry);
                Files.copy(Paths.get(file), zos);
            } catch (IOException ex) {
                Logger.getLogger(CreateZipFileFromDirectory.class.getName()).log(Level.SEVERE, null, ex);
            }
        };
        
        filesToAdd.forEach( addToZipFile );
        filesToUpdate.forEach( addToZipFile );
        originalZipFile.close();
        zos.close();
        Files.move(tempFile.toPath(), originalFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    }
}

9. LZMA2 Algoritması ile Dosya Sıkıştırmak ve Açmak

LZMA2 algoritması ile daha fazla kayıpsız sıkıştırma elde etmek mümkündür. En yüksek sıkıştırma oranı sağlayan seçeneklerle çalıştırıldığında, sıkıştırma süresi uzun ve bellek kullanımı yüksek olsa da büyük dosyaları sıkıştırmak istiyorsanız LZMA2'yı tercih etmelisiniz. LZMA2 ile birlikte sıkıştırma dosyasının uzantısı xz olacaktır. Java uygulamasından bu formatta dosya sıkıştırmak için tukaani kütüphanesini kullanabilirsiniz. Bu amaçla geliştireceğiniz maven projesinde pom.xml dosyasına aşağıdaki bağımlılığı eklemeniz gerekir:

<dependency>
   <groupId>org.tukaani</groupId>
   <artifactId>xz</artifactId>
   <version>1.6</version>
</dependency>

Java'da Heap yığın (=Heap dump) dosyaları diskte bolca yer kaplar. Bu dosyaları diskten yer kazanabilmek amacıyla xz formatında saklayabiliriz:

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example1 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path heapDumpFile = Paths.get("c:/tmp/dump.hprof");
        final File xzHeapDumpFile = new File("c:/tmp", "dump.hprof.xz");
        final FileOutputStream xzOutput = new FileOutputStream(xzHeapDumpFile);
        final LZMA2Options lzmA2Options = new LZMA2Options(LZMA2Options.PRESET_DEFAULT);
        try (XZOutputStream xzos = new XZOutputStream(xzOutput, lzmA2Options)) {
            Files.copy(heapDumpFile, xzos);
            long originalFileSize = heapDumpFile.toFile().length();
            long xzFileSize = xzHeapDumpFile.length();
            System.err.println("Heap dump file size: " + (originalFileSize / (1024 * 1024)) + "MB.");
            System.err.println("xz file size: " + (xzFileSize / (1024 * 1024)) + "MB.");
            System.err.println("Compression ratio: " + ((xzFileSize * 100) / originalFileSize) + "%.");
        }
    }
}

Uygulamayı çalıştırdığımızda ekran çıktısı aşağıda verildiği gibidir:

Heap dump file size: 256MB.
xz file size: 33MB.
Compression ratio: 12%

Java uygulamasından xz formatındaki sıkıştırılmış dosyadan geri açmak için aşağıdaki örnekten yararlanabilirsiniz:

package com.example;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.tukaani.xz.XZInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example2 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path heapDumpFile = Paths.get("c:/tmp/dump.hprof");
        final File xzHeapDumpFile = new File("c:/tmp", "dump.hprof.xz");
        final FileInputStream xzInput = new FileInputStream(xzHeapDumpFile);
        try (XZInputStream xzis = new XZInputStream(xzInput)) {
            Files.copy(xzis,heapDumpFile);
        }
    }
}

10. Apache Commons Compress ile Dizini Arşivlemek ve LZMA2 Algoritması ile Sıkıştırmak

LZMA2 sıkıştırma algoritması bir dizini arşivlemek için kullanılamaz. Arşivlemek için TAR formatından yararlanabiliriz. TAR birden fazla dizin ve dosyayı tek bir dosyada birleştirerek bir dizini arşivlememizi sağlar. TAR bir sıkıştırma formatı ya da algoritması değildir. Aşağıda verilen kodda önce Apache Commons Compress kütüphanesini kullanarak bir dizini ve dizin içindeki dosyaları, TAR formatında arşivleyeceğiz. TAR formatında arşivlediğimiz dosyayı xz formatında sıkıştırıracağız ve böylelikle diskte daha az yer kaplayacak. Maven projesi olarak geliştirdiğimiz projenin pom.xml dosyasına aşağıdaki bağımlılıkları eklememiz gerekir:

<dependency>
   <groupId>org.tukaani</groupId>
   <artifactId>xz</artifactId>
   <version>1.6</version>
</dependency>
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-compress</artifactId>
   <version>1.14</version>
</dependency>

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example3 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        FileOutputStream fos = new FileOutputStream(tarXZFile);
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(new XZOutputStream(fos, new LZMA2Options(LZMA2Options.PRESET_MIN)))){
            taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            final Path directory = Paths.get("c:/tmp/figures");
            addFilesToTar(taos, directory.toFile());
        }
    }

    private static void addFilesToTar(TarArchiveOutputStream taos, File file) throws IOException {
        taos.putArchiveEntry(new TarArchiveEntry(file));
        if (file.isFile()) {
            Files.copy(file.toPath(), taos);
            taos.closeArchiveEntry();
        } else if (file.isDirectory()) {
            taos.closeArchiveEntry();
            for (File child : file.listFiles()) {
                addFilesToTar(taos, child);
            }
        }
    }
}

LZMA2 ile sıkıştırma işlemini Apache Commons Compress kütüphanesi içinden çıkan XZCompressorOutputStream sınıfı ile de gerçekleştirebiliriz:

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example4 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        FileOutputStream fos = new FileOutputStream(tarXZFile);
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(new XZCompressorOutputStream(fos))){
            taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            final Path directory = Paths.get("c:/tmp/figures");
            addFilesToTar(taos, directory.toFile());
        }
    }

    private static void addFilesToTar(TarArchiveOutputStream taos, File file) throws IOException {
        taos.putArchiveEntry(new TarArchiveEntry(file));
        if (file.isFile()) {
            Files.copy(file.toPath(), taos);
            taos.closeArchiveEntry();
        } else if (file.isDirectory()) {
            taos.closeArchiveEntry();
            for (File child : file.listFiles()) {
                addFilesToTar(taos, child);
            }
        }
    }
}

TAR olarak arşivlenmiş ve LZMA2 ile sıkıştırılmış dosyayı (figures.tar.xz), arşiv dosyasından tekrar geri açmak için aşağıdaki koddan yararlanabilirsiniz:

package com.example;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example5 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        String targetDirectory= "c:/";
        try (
            final FileInputStream fis = new FileInputStream(tarXZFile);
            final BufferedInputStream bis= new BufferedInputStream(fis);
            final XZCompressorInputStream xzCompressorInputStream = new XZCompressorInputStream(bis);
            final TarArchiveInputStream tais = new TarArchiveInputStream(xzCompressorInputStream);
        ) {
            TarArchiveEntry tarEntry = tais.getNextTarEntry();
            while(Objects.nonNull(tarEntry)) {
                final File target= new File(targetDirectory,tarEntry.getName());
                if (tarEntry.isDirectory()) {
                    target.mkdirs();
                }else if (tarEntry.isFile()){
                    Files.copy(tais, target.toPath(),StandardCopyOption.REPLACE_EXISTING );
                }
                tarEntry = tais.getNextTarEntry();                   
            }
        }
    }

}