Java로 Builder Program 만들기 (완결) - Apache Commons-compress로 빌드 된 프로젝트 폴더 tar.gz 파일로 압축하기

이전 포스팅에서 원하는 도커 이미지를 사용하여 Build 된 프로젝트 폴더를 AWS S3 Bucket에 올리기 쉽게 tar.gz(tgz) 파일로 압축하는 작업을 실행할 차례. apache에서 제공하는 commons-compress 라이브러리를 주입받아 사용한다.


Dependency injection

본인은 gadle build tool을 사용하고, commons-compress 1.3버전을 주입받아 사용하였다.

dependencies {
    implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.3'
}

 

 

Make CompressLib Class

compress 기능은 추후 다른 곳에서도 사용할 수 있기 때문에 compress 기능을 별도로 관리하는 CompressLib Class를 만들어 기능을 여기에 구현한다. 현재 만들고 있는 프로그램은 싱글 쓰레드로 구성되어 있기 때문에 모든 util, lib Class는 싱글턴 패턴으로 개발하여 인스턴스 생성에 따른 무분별한 hip 메모리 사용을 방지한다.

package mingyu.libraries;

import org.apache.commons.compress.archivers.ArchiveEntry;
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.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class CompressLib {

    private static CompressLib instance;

    public static CompressLib getInstance() {
        if (instance == null) {
            synchronized (CompressLib.class) {
                instance = new CompressLib();
            }
        }
        return instance;
    }

    // compress(압축) 하기
    public void compress(Path source) throws IOException {

        if (!Files.isDirectory(source)) {
            throw new IOException("Please provide a directory.");
        }

        // 압축 진행할 폴더 이름을 gzip 파일 폴더 이름으로 정한다
        String tarFileName = source.getFileName().toString() + ".tar.gz";

        try (OutputStream fOut = Files.newOutputStream(Paths.get(tarFileName));
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

            Files.walkFileTree(source, new SimpleFileVisitor<>() {

                @Override
                public FileVisitResult visitFile(Path file,
                                                 BasicFileAttributes attributes) {

                    // only copy files, no symbolic links
                    if (attributes.isSymbolicLink()) {
                        return FileVisitResult.CONTINUE;
                    }

                    // get filename
                    Path targetFile = source.relativize(file);

                    try {
                        TarArchiveEntry tarEntry = new TarArchiveEntry(
                                file.toFile(), targetFile.toString());

                        tOut.putArchiveEntry(tarEntry);

                        Files.copy(file, tOut);

                        tOut.closeArchiveEntry();

                        System.out.printf("file : %s%n", file);

                    } catch (IOException e) {
                        System.err.printf("Unable to tar.gz : %s%n%s%n", file, e);
                    }

                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    System.err.printf("Unable to tar.gz : %s%n%s%n", file, exc);
                    return FileVisitResult.CONTINUE;
                }

            });

            tOut.finish();
        }

    }
}

 

Make ArchiveManager Class

위에서 구현한 CompressLib의 압축 기능을 실제로 사용하는 ArichiveManager Class를 만든다. 해당 클래스의 startArchive method에서 압축을 실행한다.

package mingyu.classes;

import mingyu.libraries.CompressLib;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.file.Path;
import java.nio.file.Paths;

public class ArchiveManager {

    private Logger logger;
    private CompressLib compressLib;
    public ArchiveManager() {
        logger = LoggerFactory.getLogger("ArchiveManagerLogger");
        compressLib = CompressLib.getInstance();
    }

    public void startArchive(String path) {
        try {
            Path source = Paths.get(path);
            compressLib.compress(source);
        } catch (Exception exception) {
            logger.info("startArchive Error ::\n",exception.getMessage(),exception.getStackTrace());
        }

    }

}

 

Update BuilderManager Class

위의 ArchiveManager 객체를 생성해서 starArchive() method를 실행하는 문구를 추가하고 Run 작업을 실시한다.

public static void main(String[] args) throws Exception {
        // args[0] : "git repository url" when use git pull command
        // args[1] : "dockerRepo/dockerImage:tag" when use build
        // args[2] : CI_COMMIT_BRANCH

        String gitRepositoryURL = args[0];
        String dockerRepositoryImage = args[1];
        String gitCommitBranch = args[2];

        BuilderManager builderManager = new BuilderManager(gitRepositoryURL, dockerRepositoryImage, gitCommitBranch);

        // 해당 날짜 지정
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String dateTime = dateFormat.format(new Date());
//        String sourceDirectoryPath = "/Users/mingyukim/tmp/build/source/" + dateTime;

        // 최신 source code 다운로드
//        builderManager.gitManager.sourceCodeDownload(dateTime,  sourceDirectoryPath);

        // 도커로 다운로드한 최신 source code build 하기;
//        builderManager.dockerManager.startBuild(sourceDirectoryPath);

        // build된 프로젝트 폴더 tar.gz 파일로 압축하기
        // Test Folder
        String sourceDirectoryPath = "/Users/mingyukim/tmp/build/source/20221228164126";
        builderManager.archiveManager.startArchive(sourceDirectoryPath);

    }

 

결과

해당 빌드된 폴더 ("/Users/mingyukim/tmp/build/source/20221228164126")의 내용이 tar.gz파일로 변환될 때 모든 내용이 잘 포함되었는지 출력하는 System.out.println 구문이 잘 출력되었고 BUILD SUCCESSFUL 까지 4초 걸렸으며 해당 tgz 파일이 잘 만들어지는 것 또한 확인이 가능했다.

 

Java로 만드는 Builder Program 포스팅은 여기서 마무리 짓겠다. 도커 이미지 만드는건 (2) Docker 구문에서 build & Push만 하면 되는것이기 때문에 별도로 진행하지 않았다.

 

CI/CD 구현에 있어서 파이프라인 구성을 최소화 하고 코스트를 아끼기 위해 거의 모든 처리 과정을 스스로 개발하며 재밌었다. 업무라고 생각되지 않았고 놀이라고 생각이 들어서 그런것일까?

 

Java로 더 많은 것들을 구현해보고 싶어졌다.