Mercurial > hg > heapstats
view analyzer/core/src/main/java/jp/co/ntt/oss/heapstats/container/log/ArchiveData.java @ 254:b65ab2ba6cd8
Bug 3457: Object.finalize() should not be used
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/122
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Tue, 31 Oct 2017 16:22:46 +0900 |
parents | 55773172374f |
children | 7c3bea2249ab |
line wrap: on
line source
/* * Copyright (C) 2014-2017 Yasumasa Suenaga * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package jp.co.ntt.oss.heapstats.container.log; import java.io.*; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; /** * This class stores archive data. * * @author Yasumasa Suenaga */ public class ArchiveData { private static final int INDEX_LOCAL_ADDR = 1; private static final int INDEX_FOREIGN_ADDR = 2; private static final int INDEX_STATE = 3; private static final int INDEX_QUEUE = 4; /** Represents the index value of the inode of the file socket endpoint. */ private static final int INDEX_INODE = 9; private final LocalDateTime date; private final String archivePath; private File extractPath; private Map<String, String> envInfo; private List<String> tcp; private List<String> tcp6; private List<String> udp; private List<String> udp6; private List<String> sockOwner; private boolean parsed; private static ReferenceQueue<ArchiveData> refQueue; private static class PhantomRefWrapper extends PhantomReference<ArchiveData> { private final Path deleteTarget; public PhantomRefWrapper(ArchiveData ref) { super(ref, refQueue); Objects.requireNonNull(ref.extractPath, "extractPath must not be null."); this.deleteTarget = ref.extractPath.toPath(); } public void clean() { try { Files.walkFileTree(deleteTarget, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { Files.delete(dir); return FileVisitResult.CONTINUE; } else { throw e; } } }); } catch (IOException e) { throw new UncheckedIOException(e); } } } private static Set<PhantomRefWrapper> refSet; private static void cleaner() { while (true) { try { PhantomRefWrapper ref = (PhantomRefWrapper) refQueue.remove(); ref.clean(); refSet.remove(ref); } catch (InterruptedException e) { // Do nothing. } } } static { refQueue = new ReferenceQueue<>(); refSet = Collections.synchronizedSet(new HashSet<>()); Thread th = new Thread(ArchiveData::cleaner, "ArchiveData cleaner"); th.setDaemon(true); th.start(); } /** * Constructor of ArchiveData. * * @param log LogData. This value must be included archive data. * @throws java.io.IOException if heapstats_archive is not found */ public ArchiveData(LogData log) throws IOException{ this(log, null); extractPath = Files.createTempDirectory("heapstats_archive").toFile(); extractPath.deleteOnExit(); refSet.add(new PhantomRefWrapper(this)); } /** * Constructor of ArchiveData. * * @param log LogData. This value must be included archive data. * @param location Location to extract archive data. */ public ArchiveData(LogData log, File location){ date = log.getDateTime(); archivePath = log.getArchivePath(); extractPath = location; parsed = false; if (extractPath != null) { refSet.add(new PhantomRefWrapper(this)); } } /** * Build environment info from envInfo.txt . * * @param archive HeapStats ZIP archive. * @param entry ZipEntry of methodInfo. */ private void buildEnvInfo(ZipFile archive, ZipEntry entry) throws IOException{ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); try(InputStream in = archive.getInputStream(entry)){ Properties prop = new Properties(); prop.load(in); prop.computeIfPresent("CollectionDate", (k, v) -> formatter.format( LocalDateTime.ofInstant(Instant.ofEpochMilli( Long.parseLong((String)v)), ZoneId.systemDefault()))); String[] triggers = {"Resource Exhausted", "Signal", "Interval", "Deadlock"}; prop.computeIfPresent("LogTrigger", (k, v) -> triggers[Integer.parseInt((String)v) - 1]); envInfo = new HashMap<>(); envInfo.put("archive", archivePath); prop.forEach((k, v) -> envInfo.put((String)k, (String)v)); } } /** * Build String data from ZIP entry * * @param archive HeapStats ZIP archive. * @param entry ZipEntry to be parsed. * @return String value from ZipEntry. * @throws java.io.IOException */ private List<String> buildStringData(ZipFile archive, ZipEntry entry) throws IOException{ try(BufferedReader reader = new BufferedReader(new InputStreamReader(archive.getInputStream(entry)))){ return reader.lines() .skip(1) .map(s -> s.trim()) .collect(Collectors.toList()); } } /** * Deflating file in ZIP. * * @param archive HeapStats ZIP archive. * @param entry ZipEntry to be deflated. * @throws java.io.IOException */ private void deflateFileData(ZipFile archive, ZipEntry entry) throws IOException{ Path destPath = Paths.get(extractPath.getAbsolutePath(), entry.getName()); try(BufferedReader reader = new BufferedReader(new InputStreamReader(archive.getInputStream(entry))); PrintWriter writer = new PrintWriter(Files.newBufferedWriter(destPath, StandardOpenOption.CREATE));){ reader.lines() .map(s -> s.replace('\0', ' ')) .forEach(writer::println); } } /** * Get IPv4 data. * * @param data String data in procfs. * @return IPv4 address. */ private String getIPv4(String data){ StringJoiner joiner = (new StringJoiner(".")) .add(Integer.valueOf(data.substring(6, 8), 16).toString()) .add(Integer.valueOf(data.substring(4, 6), 16).toString()) .add(Integer.valueOf(data.substring(2, 4), 16).toString()) .add(Integer.valueOf(data.substring(0, 2), 16).toString()); return joiner.toString(); } /** * Get IPv6 data. * * @param data String data in procfs. * @return IPv4 address. */ private String getIPv6(String data){ StringJoiner joiner = (new StringJoiner(":")) .add(data.substring(6, 8) + data.substring(4, 6)) .add(data.substring(2, 4) + data.substring(0, 2)) .add(data.substring(14, 16) + data.substring(12, 14)) .add(data.substring(10, 12) + data.substring(8, 10)) .add(data.substring(22, 24) + data.substring(20, 22)) .add(data.substring(18, 20) + data.substring(16, 18)) .add(data.substring(26, 28) + data.substring(24, 26)); return joiner.toString(); } /** * Write socket data. * * @param proto Protocol. tcp or udp. * @param data Socket owner data. * @param writer PrintWriter to write. * @param isIPv4 true if this arguments represent IPv4. */ private void writeSockDataInternal(String proto, String[] data, PrintWriter writer, boolean isIPv4){ writer.print(String.format("%-7s", sockOwner.contains(data[INDEX_INODE]) ? "jvm" : "")); // owner writer.print(String.format("%-7s", proto)); String[] queueData = data[INDEX_QUEUE].split(":"); writer.print(String.format("%-8d", Integer.parseInt(queueData[1], 16))); // Recv-Q writer.print(String.format("%-8d", Integer.parseInt(queueData[0], 16))); // Send-Q String[] localAddr = data[INDEX_LOCAL_ADDR].split(":"); // local address String localAddrStr = isIPv4 ? getIPv4(localAddr[0]) : getIPv6(localAddr[0]); localAddrStr += String.format(":%d", Integer.parseInt(localAddr[1], 16)); writer.print(String.format("%-42s", localAddrStr)); String[] foreignAddr = data[INDEX_FOREIGN_ADDR].split(":"); // foreign address String foreignAddrStr = isIPv4 ? getIPv4(foreignAddr[0]) : getIPv6(foreignAddr[0]); foreignAddrStr += String.format(":%d", Integer.parseInt(foreignAddr[1], 16)); writer.print(String.format("%-42s", foreignAddrStr)); try{ String[] states = {"ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING"}; writer.println(states[Integer.parseInt(data[INDEX_STATE], 16) - 1]); // connection state } catch(ArrayIndexOutOfBoundsException e){ writer.println("-"); } } /** * Build socket data from archive data. * * @throws IOException */ private void buildSockData() throws IOException{ Path sockfile = Paths.get(extractPath.getAbsolutePath(), "socket"); try(PrintWriter writer = new PrintWriter(Files.newOutputStream(sockfile, StandardOpenOption.CREATE))){ writer.print(String.format("%-7s", "Owner")); writer.print(String.format("%-7s", "Proto")); writer.print(String.format("%-8s", "Recv-Q")); writer.print(String.format("%-8s", "Send-Q")); writer.print(String.format("%-42s", "Local Address")); writer.print(String.format("%-42s", "Foreign Address")); writer.println("State"); Optional.ofNullable(tcp) .ifPresent(l -> l.stream() .map(s -> s.split("\\s+")) .forEach(d -> writeSockDataInternal("tcp", d, writer, true))); Optional.ofNullable(tcp6) .ifPresent(l -> l.stream() .map(s -> s.split("\\s+")) .forEach(d -> writeSockDataInternal("tcp6", d, writer, false))); Optional.ofNullable(udp) .ifPresent(l -> l.stream() .map(s -> s.split("\\s+")) .forEach(d -> writeSockDataInternal("udp", d, writer, true))); Optional.ofNullable(udp6) .ifPresent(l -> l.stream() .map(s -> s.split("\\s+")) .forEach(d -> writeSockDataInternal("udp6", d, writer, false))); } } /** * Build zip entry. * * @throws IOException */ private void processZipEntry(ZipFile archive, ZipEntry entry) throws IOException{ switch(entry.getName()){ case "envInfo.txt": buildEnvInfo(archive, entry); break; case "tcp": tcp = buildStringData(archive, entry); break; case "tcp6": tcp6 = buildStringData(archive, entry); break; case "udp": udp = buildStringData(archive, entry); break; case "udp6": udp6 = buildStringData(archive, entry); break; case "sockowner": sockOwner = buildStringData(archive, entry); break; default: deflateFileData(archive, entry); break; } } /** * Parsing Archive data. * @throws java.io.IOException if could not read or/and write archive. */ public void parseArchive() throws IOException{ if(parsed){ return; } try(ZipFile archive = new ZipFile(archivePath)){ archive.stream().forEach(new ConsumerWrapper<ZipEntry>(d -> processZipEntry(archive, d))); buildSockData(); List<String> envInfoStrings = envInfo.entrySet() .stream() .map(e -> e.getKey() + " = " + e.getValue()) .collect(Collectors.toList()); Files.write(Paths.get(extractPath.getAbsolutePath(), "envInfo.txt"), envInfoStrings, StandardOpenOption.CREATE); } parsed = true; } /** * Getter of this date this archive is created. * @return LocalDateTime this archive is created. */ public LocalDateTime getDate() { return date; } /** * Getter of envInfo. * * @return envInfo in this archive. */ public Map<String, String> getEnvInfo() { return envInfo; } /** * Getter of file list in this archive. * This list includes all deflated files in archive. * * @return file list in this archive. */ public List<String> getFileList(){ return Arrays.asList(extractPath.list()); } /** * Getter of location to archive data. * * @return Path of archive data. */ public File getExtractPath() { return extractPath; } /** * Getter of file contents. * Contents is represented as String. * * @param file File to be got. * @return Contents of file. * @throws IOException if file not found. */ public String getFileContents(String file) throws IOException{ Path filePath = Paths.get(extractPath.getAbsolutePath(), file); try(Stream<String> paths = Files.lines(filePath)){ return paths.collect(Collectors.joining("\n")); } } }