001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.util; 020 021import org.jpos.q2.Q2; 022 023import java.io.File; 024import java.util.concurrent.LinkedBlockingQueue; 025import java.util.concurrent.ThreadPoolExecutor; 026import java.util.concurrent.TimeUnit; 027 028/** 029 * Serializes log file compression across all log listener instances 030 * through a single on-demand background worker. 031 * 032 * <p>The worker thread is created on demand, runs as a low-priority daemon, 033 * and exits after {@link #IDLE_TIMEOUT} milliseconds of inactivity.</p> 034 */ 035public class LogCompressor { 036 private static final long IDLE_TIMEOUT = 30_000L; 037 038 private static volatile LogCompressor instance; 039 private final ThreadPoolExecutor executor = new ThreadPoolExecutor( 040 0, 041 1, 042 IDLE_TIMEOUT, 043 TimeUnit.MILLISECONDS, 044 new LinkedBlockingQueue<>(), 045 r -> { 046 Thread t = Thread.ofPlatform() 047 .daemon(true) 048 .name("log-compressor", 0) 049 .unstarted(r); 050 t.setPriority(Thread.NORM_PRIORITY - 1); 051 return t; 052 } 053 ); 054 055 private LogCompressor() { 056 executor.allowCoreThreadTimeOut(true); 057 } 058 059 /** 060 * Returns the lazy-initialised singleton. 061 * 062 * @return the shared {@link LogCompressor} 063 */ 064 public static LogCompressor getInstance() { 065 if (instance == null) { 066 synchronized (LogCompressor.class) { 067 if (instance == null) 068 instance = new LogCompressor(); 069 } 070 } 071 return instance; 072 } 073 074 /** 075 * Submits a compression task for {@code logFile} on the shared executor. 076 * 077 * @param logFile log file being compressed (used for diagnostic logging) 078 * @param task compression task to run 079 */ 080 public void submit(File logFile, Runnable task) { 081 executor.execute(() -> { 082 try { 083 task.run(); 084 } catch (Throwable t) { 085 Q2.getQ2().getLog().warn( 086 String.format("error running log compression task for '%s'", logFile), 087 t 088 ); 089 } 090 }); 091 } 092}