com.github.ukase.service.BulkRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.github.ukase.service.BulkRenderer.java

Source

/*
 * Copyright (c) 2016 Konstantin Lepa <konstantin+ukase@lepabox.net>
 *
 * This file is part of Ukase.
 *
 *  Ukase is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *   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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.github.ukase.service;

import com.github.ukase.bulk.BulkRenderTask;
import com.github.ukase.bulk.BulkStatus;
import com.github.ukase.toolkit.RenderTask;
import com.github.ukase.toolkit.RenderTaskBuilder;
import com.github.ukase.web.UkasePayload;
import lombok.extern.log4j.Log4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

@Service
@Log4j
public class BulkRenderer {
    private static final String PDF_EXT = ".pdf";
    private static final int SUB_DIR_NAME_LENGTH = 8;

    private final ConcurrentMap<String, Long> renderedPDFs = new ConcurrentHashMap<>();

    @Autowired
    private ExecutorService executor;
    @Autowired
    private RenderTaskBuilder builder;
    @Autowired
    private Long ttl;
    private File path;

    @Autowired
    public void setPath(File path) {
        this.path = path;
        checkDataDirectory();
    }

    public String putTaskInOrder(List<UkasePayload> payloads) throws IOException {
        return startProcessing(payloads).getId();
    }

    public byte[] processOrder(List<UkasePayload> payloads) throws IOException, InterruptedException {
        return startProcessing(payloads).getResult();
    }

    public BulkStatus checkStatus(String id) {
        Long rendered = renderedPDFs.get(id);
        if (rendered == null) {
            return BulkStatus.ERROR;
        } else if (rendered.equals(Long.MAX_VALUE)) {
            return BulkStatus.ORDERED;
        }
        return BulkStatus.PROCESSED;
    }

    public byte[] getOrder(String id) {
        if (checkStatus(id) == BulkStatus.PROCESSED) {
            return getFileData(id);
        }
        return null;
    }

    public void clearOldPDFs() {
        renderedPDFs.forEach(this::checkTTL);
    }

    private void checkTTL(String id, Long created) {
        if (created == null || created == Long.MAX_VALUE) {
            return;
        }
        if (System.currentTimeMillis() - created > ttl) {
            File pdf = getPdfFile(id);
            if (!pdf.delete()) {
                log.warn("PDF for " + id + " weren't deleted");
            }

            renderedPDFs.remove(id);

            File subDir = pdf.getParentFile();
            File[] files = subDir.listFiles();
            if (files != null && files.length == 0) {
                log.info(subDir.getName() + " removing... " + (subDir.delete() ? "success" : "failed"));
            }
        }
    }

    private BulkRenderTask startProcessing(List<UkasePayload> payloads) throws IOException {
        List<RenderTask> tasks = payloads.stream().map(builder::build).collect(Collectors.toList());
        BulkRenderTask task = new BulkRenderTask(tasks, this::registerProcessedData, this::registerFail);
        task.getSubTasks().forEach(executor::submit);
        renderedPDFs.put(task.getId(), Long.MAX_VALUE);

        return task;
    }

    private void registerProcessedData(String id, byte[] data) {
        File subDir = getSubDir(id);
        if (!subDir.isDirectory()) {
            if (!subDir.mkdir()) {
                log.warn("Something wrong with creating pdf's subdir: " + subDir.getAbsolutePath());
            }
        }
        File pdfFile = getPdfFile(id, subDir);
        try (FileOutputStream fos = new FileOutputStream(pdfFile, false)) {
            fos.write(data);
            fos.flush();
            renderedPDFs.put(id, System.currentTimeMillis());
        } catch (IOException e) {
            log.error("Cannot create pdf file " + pdfFile.getName(), e);
            registerFail(id);
        }
    }

    private void registerFail(String id) {
        renderedPDFs.remove(id);
    }

    private void checkDataDirectory() {
        File[] directories = path.listFiles();
        if (directories == null) {
            return;
        }
        Arrays.stream(directories).forEach(this::checkSubDirectories);
    }

    private void checkSubDirectories(File subDir) {
        if (subDir.isDirectory() && subDir.getName().length() == SUB_DIR_NAME_LENGTH) {
            File[] pdfs = subDir.listFiles();
            if (pdfs == null) {
                return;
            }
            Arrays.stream(pdfs).forEach(this::registerPdf);
        }
    }

    private void registerPdf(File pdf) {
        String fileName = pdf.getName();
        if (pdf.isFile() && fileName.endsWith(PDF_EXT)) {
            String id = fileName.substring(0, fileName.length() - 4);
            renderedPDFs.put(id, pdf.lastModified());
        }
    }

    private byte[] getFileData(String id) {
        File pdf = getPdfFile(id);
        Path pdfPath = pdf.getAbsoluteFile().toPath();
        try {
            return Files.readAllBytes(pdfPath);
        } catch (IOException e) {
            log.error("Cannot read pdf file data " + pdf.getAbsolutePath());
            return null;
        }
    }

    private File getSubDir(String id) {
        return new File(path, getSubDirName(id));
    }

    private String getSubDirName(String id) {
        return id.substring(0, SUB_DIR_NAME_LENGTH);
    }

    private File getPdfFile(String id) {
        return getPdfFile(id, getSubDir(id));
    }

    private File getPdfFile(String id, File subDir) {
        return new File(subDir, id + PDF_EXT);
    }
}