package ch.sla.jdbcperflogger.logger; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.SocketException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import ch.sla.jdbcperflogger.Logger; import ch.sla.jdbcperflogger.model.BufferFullLogMessage; import ch.sla.jdbcperflogger.model.ConnectionInfo; import ch.sla.jdbcperflogger.model.LogMessage; // public for tests public class SocketLogSender implements Runnable, LogSender { private final static Logger LOGGER2 = Logger.getLogger(SocketLogSender.class); private final BlockingQueue<LogMessage> logsToSend = new LinkedBlockingQueue<LogMessage>(10000); private final Socket socket; private final AtomicBoolean queueFull = new AtomicBoolean(); SocketLogSender(final Socket socket) throws SocketException { this.socket = socket; socket.setKeepAlive(true); socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10)); } /* (non-Javadoc) * @see ch.sla.jdbcperflogger.logger.LogSender#postLog(ch.sla.jdbcperflogger.model.LogMessage) */ @Override public void postLog(final LogMessage log) { final boolean posted = logsToSend.offer(log); if (!posted) { queueFull.set(true); LOGGER2.warn("queue full, dropping remote log of statement"); } } @Override public void run() { // first send all current connections information to the socket synchronized (PerfLoggerRemoting.connectionToInfo) { for (final ConnectionInfo connectionInfo : PerfLoggerRemoting.connectionToInfo.values()) { logsToSend.offer(connectionInfo); } } ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(socket.getOutputStream()); int cnt = 0; while (true) { try { if (queueFull.compareAndSet(true, false)) { oos.writeUnshared(new BufferFullLogMessage(System.currentTimeMillis())); } final LogMessage log = logsToSend.poll(10, TimeUnit.SECONDS); if (log != null) { oos.writeUnshared(log); } else { // check the socket state if (socket.isClosed() || !socket.isConnected()) { // client disconnected break; } oos.writeUnshared(null); } cnt = (cnt + 1) % 10; if (cnt == 0) { // avoid mem leak when the stream keeps back // references to serialized objects oos.reset(); } } catch (final InterruptedException e) { break; } } } catch (final IOException e) { LOGGER2.warn("socket error", e); } finally { LOGGER2.info("closing connection with " + socket); PerfLoggerRemoting.senders.remove(this); if (oos != null) { try { oos.close(); } catch (final IOException ignored) { } } try { socket.close(); } catch (final IOException e) { LOGGER2.error("error while closing socket", e); } } } }