001package com.credibledoc.substitution.doc.module.substitution.file; 002 003import com.credibledoc.combiner.application.Application; 004import com.credibledoc.combiner.application.ApplicationService; 005import com.credibledoc.combiner.file.FileService; 006import com.credibledoc.combiner.node.applicationlog.ApplicationLog; 007import com.credibledoc.combiner.node.file.NodeFileService; 008import com.credibledoc.substitution.core.exception.SubstitutionRuntimeException; 009import com.google.common.base.Preconditions; 010import lombok.RequiredArgsConstructor; 011import org.apache.commons.compress.archivers.ArchiveInputStream; 012import org.apache.commons.compress.archivers.ArchiveStreamFactory; 013import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; 014import org.apache.commons.compress.utils.IOUtils; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017import org.springframework.stereotype.Service; 018 019import javax.inject.Inject; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.InputStream; 024import java.util.*; 025import java.util.Map.Entry; 026 027@Service 028@RequiredArgsConstructor(onConstructor = @__(@Inject)) 029public class FileUtilService { 030 031 private static final Logger logger = LoggerFactory.getLogger(FileUtilService.class); 032 033 private static final char DOT = '.'; 034 035 private static final String REPORT_FOLDER_EXTENSION = ".report"; 036 037 /** 038 * Generate a new file with transformed content of source log files. 039 * @param fileNumber an order number. Can be 'null' if a single file 040 * should be created in the directory. If not 'null', 041 * this number will be appended to the file name, 042 * for example index_001.html 043 * @param fileName the file name without extension 044 * @param reportDirectory where the new file will be created 045 * @param fileExtension for example html or txt 046 * 047 * @return the new file with context transformed from the source log file. 048 */ 049 public File generateFile(Integer fileNumber, String fileName, File reportDirectory, String fileExtension) { 050 String num = null; 051 if (fileNumber != null) { 052 num = String.format("%03d", fileNumber); 053 } 054 StringBuilder newFileName = new StringBuilder(fileName); 055 if (num != null) { 056 newFileName.append(DOT).append(num); 057 } 058 newFileName.append(DOT).append(fileExtension); 059 File file = new File(reportDirectory, newFileName.toString()); 060 logger.info("The new empty file created: '{}'", file.getAbsolutePath()); 061 return file; 062 } 063 064 /** 065 * Sort files in a directory from the first argument. 066 * For each {@link Application} creates its own list of files. 067 * 068 * @param directory cannot be 'null'. Can have files from different {@link Application}s.<br> 069 * Cannot contain other files. But can have directories. These directories 070 * will be processed recursively. 071 * @param applicationLogs at first invocation an empty, and it will be filled with files 072 */ 073 private void collectApplicationLogs(File directory, List<ApplicationLog> applicationLogs) { 074 Preconditions.checkNotNull(directory); 075 Preconditions.checkState(directory.isDirectory()); 076 Map<Application, Map<Date, File>> map = new HashMap<>(); 077 File[] files = Objects.requireNonNull(directory.listFiles()); 078 for (File file : files) { 079 addFileToMap(applicationLogs, map, files, file); 080 } 081 ApplicationService applicationService = ApplicationService.getInstance(); 082 for (Entry<Application, Map<Date, File>> appEntry : map.entrySet()) { 083 Application application = appEntry.getKey(); 084 ApplicationLog applicationLog = applicationService.findOrCreate(applicationLogs, application); 085 NodeFileService.getInstance().appendToNodeLogs(appEntry.getValue(), applicationLog); 086 } 087 } 088 089 private void addFileToMap(List<ApplicationLog> applicationLogs, Map<Application, 090 Map<Date, File>> map, File[] files, File file) { 091 092 if (file.isFile()) { 093 if (file.getName().endsWith(".zip")) { 094 file = unzipIfNotExists(file, files); 095 } 096 Application application = FileService.getInstance().findApplication(file); 097 if (!map.containsKey(application)) { 098 map.put(application, new TreeMap<>()); 099 } 100 101 Date date = FileService.getInstance().findDate(file, application); 102 if (date == null) { 103 throw new SubstitutionRuntimeException("Cannot find a date in the file: " + file.getAbsolutePath()); 104 } 105 map.get(application).put(date, file); 106 } else { 107 // directories 108 if (!file.getName().endsWith(REPORT_FOLDER_EXTENSION)) { 109 collectApplicationLogs(file, applicationLogs); 110 } 111 } 112 } 113 114 /** 115 * If the second argument contains unzipped first argument, do not unzip it. 116 * Else unzip it and return a file from this zipFile. 117 * @param zipFile zipped log file 118 * @param files all files in a directory 119 * @return an unzipped file or file from files 120 */ 121 private File unzipIfNotExists(File zipFile, File[] files) { 122 try ( 123 InputStream is = new FileInputStream(zipFile); 124 ArchiveInputStream ais = new ArchiveStreamFactory().createArchiveInputStream("zip", is) 125 ) { 126 ZipArchiveEntry zipArchiveEntry = (ZipArchiveEntry) ais.getNextEntry(); 127 String zipArchiveEntryName = zipArchiveEntry.getName(); 128 for (File file : files) { 129 if (file.getName().equals(zipArchiveEntryName)) { 130 return file; 131 } 132 } 133 File outputFile = new File(zipFile.getParentFile(), zipArchiveEntryName); 134 logger.info("File {}:{} will be decompressed to {}", zipFile.getName(), zipArchiveEntryName, outputFile.getAbsolutePath()); 135 IOUtils.copy(ais, new FileOutputStream(outputFile)); 136 return outputFile; 137 } catch (Exception e) { 138 throw new SubstitutionRuntimeException("Cannot unzipIfNotExists file: " + zipFile.getAbsolutePath(), e); 139 } 140 } 141 142} 143