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 java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.OutputStream; 024import java.util.concurrent.Executors; 025import java.util.concurrent.ScheduledExecutorService; 026import java.util.concurrent.Semaphore; 027import java.util.concurrent.TimeUnit; 028 029/** 030 * {@link OutputStream} that buffers writes and periodically flushes the 031 * accumulated bytes as a single {@link LogEvent} after a configurable delay. 032 */ 033public class LogEventOutputStream extends OutputStream implements LogSource, Runnable { 034 private ByteArrayOutputStream baos = new ByteArrayOutputStream(); 035 private Logger logger; 036 private String realm; 037 private ScheduledExecutorService logService; 038 private Semaphore lock = new Semaphore(1); 039 private volatile LogEvent evt; 040 private long delay; 041 042 /** Default constructor. */ 043 public LogEventOutputStream() { 044 super(); 045 baos = new ByteArrayOutputStream(); 046 logService = Executors.newScheduledThreadPool(1); 047 } 048 049 /** 050 * Constructs a stream that flushes the buffered bytes as log events on the 051 * configured logger/realm after {@code delay} ms of inactivity. 052 * 053 * @param logger destination logger 054 * @param realm logger realm 055 * @param delay flush delay in milliseconds 056 */ 057 public LogEventOutputStream(Logger logger, String realm, long delay) { 058 this(); 059 this.logger = logger; 060 this.realm = realm; 061 this.delay = delay; 062 } 063 064 @Override 065 public void write(int b) throws IOException { 066 if (b == '\n') { 067 try { 068 lock.acquire(); 069 if (evt == null) { 070 evt = new LogEvent(this, ""); 071 logService.schedule(this, delay, TimeUnit.MILLISECONDS); 072 } 073 evt.addMessage(baos.toString()); 074 baos = new ByteArrayOutputStream(); 075 } catch (InterruptedException ignored) { 076 } finally { 077 lock.release(); 078 } 079 } else { 080 baos.write(b); 081 } 082 } 083 084 @Override 085 public void setLogger(Logger logger, String realm) { 086 this.logger = logger; 087 this.realm = realm; 088 } 089 090 @Override 091 public String getRealm() { 092 return realm; 093 } 094 095 @Override 096 public Logger getLogger() { 097 return logger; 098 } 099 100 @Override 101 public void run() { 102 LogEvent event = null; 103 if (evt != null) { 104 try { 105 lock.acquire(); 106 event = evt; 107 evt = null; 108 } catch (InterruptedException ignore) { 109 } finally { 110 lock.release(); 111 } 112 if (event != null) 113 Logger.log(event); 114 } 115 } 116 117 @Override 118 public void close() throws IOException { 119 super.close(); 120 try { 121 lock.acquire(); 122 logService.shutdown(); 123 } catch (InterruptedException ignore) { 124 } finally { 125 lock.release(); 126 } 127 } 128}