PackagerImpl.java

package esa.bscs.pds4.packager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;

import esa.bscs.pds4.packager.aip.AipBuilder;
import esa.bscs.pds4.packager.aip.ManifestInfo;
import esa.bscs.pds4.packager.aip.PackageInfo;
import esa.bscs.pds4.packager.exception.PackagerException;
import esa.bscs.pds4.packager.exception.PackagerMessage;
import esa.bscs.pds4.reader.Pds4Analyser;
import esa.bscs.pds4.reader.exception.Pds4Exception;
import esa.bscs.pds4.reader.model.Pds4Label;
import esa.bscs.pds4.validate.Pds4Validator;
import esa.bscs.pds4.validate.exception.ValidationException;
import esa.bscs.pds4.validate.model.ValidationMessage;

public class PackagerImpl implements Packager {

    @Autowired(required=true)
    private AipBuilder aipBuilder;
    
    @Autowired(required=true)
    private MessageSource messageSource;
    
    @Autowired(required=true)
    private Pds4Analyser analyser;
        
    @Autowired(required=true)
    private Pds4Validator validator;
        
    @Override
    public File pack(List<File> labelFiles) {

        // Check input
        
        if(labelFiles == null || labelFiles.isEmpty()) {
            throw new IllegalArgumentException(); 
        }        
        
        // Get metadata representation
                
        final List<Pds4Label> pds4Labels = parseLabelFiles(labelFiles);
        
        // Validate products
        
        final List<ValidationMessage> messages = validator.validate(pds4Labels);
        
        if(!messages.isEmpty()) {
            throw new ValidationException(messages);
        }
        
        // Build archive information product
        
        final PackageInfo packageInfo = aipBuilder.buildArchiveInformationProduct(pds4Labels);
        
        // Generate tar.gz
        
        return generatePackage(packageInfo);        
    }
        
    private File generatePackage(PackageInfo packageInfo) {
        
        // Generate tar.gz with the proper name in tmp directory
        
        final String basename = packageInfo.getBasename();
        final File result = new File(System.getProperty("java.io.tmpdir"), basename + ".tar.gz");
        
        try (TarArchiveOutputStream stream = new TarArchiveOutputStream(new GzipCompressorOutputStream(new FileOutputStream(result)))) {
            
            final Map<File, String> entries = packageInfo.getPackageEntries();
            final ManifestInfo checksumInfo = packageInfo.getChecksumInfo();
            final ManifestInfo transferInfo = packageInfo.getTransferInfo();
            
            // Set stream to accept long paths (more than 100 bytes)
            
            stream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            
            // Add products to package 
            
            for(Map.Entry<File, String> entry : entries.entrySet()) {                
                try(InputStream inputStream = entry.getKey().toURI().toURL().openStream()) {                
                    
                    stream.putArchiveEntry(stream.createArchiveEntry(entry.getKey(), basename + "/" + entry.getValue()));
                    IOUtils.copy(inputStream, stream);
                    stream.closeArchiveEntry();
                    stream.flush();
                }
            }
                        
            // Add checksum manifest
                        
            TarArchiveEntry entry;
            
            entry = (TarArchiveEntry) stream.createArchiveEntry(getFileFor(checksumInfo.getFilename()), basename + "/" + checksumInfo.getFilename());            
            entry.setSize(checksumInfo.getContent().getBytes().length);
            
            stream.putArchiveEntry(entry);
            stream.write(checksumInfo.getContent().getBytes());
            stream.closeArchiveEntry();
            stream.flush();
            
            // Add transfer manifest

            entry = (TarArchiveEntry) stream.createArchiveEntry(getFileFor(transferInfo.getFilename()), basename + "/" + transferInfo.getFilename());            
            entry.setSize(transferInfo.getContent().getBytes().length);
            
            stream.putArchiveEntry(entry);
            stream.write(transferInfo.getContent().getBytes());                
            stream.closeArchiveEntry();
            stream.flush();

            // Add delivery label
            
            entry = (TarArchiveEntry) stream.createArchiveEntry(getFileFor(packageInfo.getFilename()), basename + "/" + packageInfo.getFilename());
            entry.setSize(packageInfo.getContent().getBytes().length);
            
            stream.putArchiveEntry(entry);
            stream.write(packageInfo.getContent().getBytes());                
            stream.closeArchiveEntry();
            stream.flush();
            
            return result;
        
        } catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }    
    
    @SuppressWarnings("java:S1166")
    private List<Pds4Label> parseLabelFiles(List<File> labelFiles) {
        
        final List<Pds4Label> pds4Labels = new ArrayList<>();
        final List<PackagerMessage> messages = new ArrayList<>();
        
        for(File labelFile : labelFiles) {

            try {
                pds4Labels.add(analyser.analyse(labelFile.toURI().toURL()));
            
            } catch(Pds4Exception e) {
                final String name = e.getLabelLocation().getFile().contains("/") ? e.getLabelLocation().getFile().substring(e.getLabelLocation().getFile().lastIndexOf('/')) : e.getLabelLocation().getFile();
                messages.add(new PackagerMessage("error.packager.parseLabel" , messageSource.getMessage("error.packager.parseLabel", new String[] {name, e.getCause().getMessage()}, Locale.getDefault())));
            
            } catch(MalformedURLException e) {
                throw new IllegalArgumentException(e);
            }
        }
        
        if(!messages.isEmpty()) {
            throw new PackagerException(messages);
        }
        
        return pds4Labels;
    }
    
    private File getFileFor(String filename) {
        
        final Path path = Paths.get(new File(filename).toURI());
        
        return path.toFile();
    }
}