/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.byteman.sockets.impl;
import org.cliffc.high_scale_lib.NonBlockingHashSet;
import org.helios.apmrouter.jmx.ConfigurationHelper;
import org.helios.apmrouter.nativex.APMSigar;
import org.helios.apmrouter.util.SimpleLogger;
import org.helios.apmrouter.util.SimpleLogger.Level;
import org.hyperic.sigar.NetStat;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
/**
* <p>Title: EmptySocketTracker</p>
* <p>Description: An empty {@link ISocketTracker} for extending.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.byteman.sockets.impl.EmptySocketTracker</code></p>
*/
public class EmptySocketTracker implements ISocketTracker, ThreadFactory {
/** A hashset of server side sockets */
protected final NonBlockingHashSet<ISocketImpl> serverSideSockets = new NonBlockingHashSet<ISocketImpl>();
/** The harvester thread */
protected Thread harvesterThread;
/** The name of the active tracker */
protected String activeTracker = null;
/** The keep running flag */
protected final AtomicBoolean keepRunning = new AtomicBoolean(false);
/** Serial number generator for harvester threads */
protected static final AtomicLong serial = new AtomicLong(0L);
/** Harvester thread group */
protected static final ThreadGroup harvesterThreadGroup = new ThreadGroup("SocketTrackingHarvesters");
/** The harvester sleep period in ms. */
protected final AtomicLong harvesterSleep = new AtomicLong(-1L);
/** The logging level for the tracker */
protected Level loggingLevel = Level.INFO;
/** Native OS API */
protected static final APMSigar sigar = APMSigar.getInstance();
/**
* Creates a new EmptySocketTracker
*/
public EmptySocketTracker() {
harvesterSleep.set(ConfigurationHelper.getLongSystemThenEnvProperty(SOCKET_HARVESTER_PERIOD_PROP, DEFAULT_SOCKET_HARVESTER_PERIOD));
}
/**
* {@inheritDoc}
* @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
*/
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(harvesterThreadGroup, this, getClass().getSimpleName() + "-HarvesterThread#" + serial.incrementAndGet());
t.setDaemon(true);
t.setPriority(Thread.NORM_PRIORITY-1);
return t;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#setActiveTracker(java.lang.String)
*/
@Override
public void setActiveTracker(String simpleName) {
activeTracker = simpleName;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#getActiveTracker()
*/
@Override
public String getActiveTracker() {
return activeTracker;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSetEOF(java.io.InputStream, java.lang.Object, boolean)
*/
@Override
public void onSetEOF(InputStream is, Object socket, boolean eof) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSkip(java.io.InputStream, long, java.lang.Object, long)
*/
@Override
public void onSkip(InputStream is, long skipped, Object socket, long skip) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSocketWrite(java.io.OutputStream, java.lang.Object, byte[], int, int)
*/
@Override
public void onSocketWrite(OutputStream os, Object socket, byte[] b,
int off, int len) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onRead(java.io.InputStream, int, java.lang.Object, byte[])
*/
@Override
public void onRead(InputStream is, int actualBytesRead, Object socket,
byte[] buffer) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onRead(java.io.InputStream, int, java.lang.Object, byte[], int, int)
*/
@Override
public void onRead(InputStream is, int actualBytesRead, Object socket,
byte[] buffer, int off, int length) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onRead(java.io.InputStream, int, java.lang.Object)
*/
@Override
public void onRead(InputStream is, int value, Object socket) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onConnect(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.net.SocketAddress, int)
*/
@Override
public void onConnect(ISocketImpl socketImpl, SocketAddress address,
int timeout) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onConnect(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.net.InetAddress, int)
*/
@Override
public void onConnect(ISocketImpl socketImpl, InetAddress address,
int timeout) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onConnect(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.lang.String, int)
*/
@Override
public void onConnect(ISocketImpl socketImpl, String host, int port) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onBind(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.net.InetAddress, int)
*/
@Override
public void onBind(ISocketImpl socketImpl, InetAddress host, int port) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onListen(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, int)
*/
@Override
public void onListen(ISocketImpl socketImpl, int backlog) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onAccept(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, org.helios.apmrouter.byteman.sockets.impl.ISocketImpl)
*/
@Override
public void onAccept(ISocketImpl socketImpl, ISocketImpl acceptedSocketImpl) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onGetInputStream(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.io.InputStream)
*/
@Override
public void onGetInputStream(ISocketImpl socketImpl, InputStream inputStream) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onGetOutputStream(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.io.OutputStream)
*/
@Override
public void onGetOutputStream(ISocketImpl socketImpl,
OutputStream outputStream) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onAvailable(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, int)
*/
@Override
public void onAvailable(ISocketImpl socketImpl, int available) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onClose(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl)
*/
@Override
public void onClose(ISocketImpl socketImpl) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onShutdownInput(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl)
*/
@Override
public void onShutdownInput(ISocketImpl socketImpl) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onShutdownOutput(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl)
*/
@Override
public void onShutdownOutput(ISocketImpl socketImpl) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSendUrgentData(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, int)
*/
@Override
public void onSendUrgentData(ISocketImpl socketImpl, int data) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSetSocket(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.lang.Object)
*/
@Override
public void onSetSocket(ISocketImpl socketImpl, Object socket) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSetServerSocket(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, java.lang.Object)
*/
@Override
public void onSetServerSocket(ISocketImpl socketImpl, Object serverSocket) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onReset(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl)
*/
@Override
public void onReset(ISocketImpl socketImpl) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#onSetPerformancePreferences(org.helios.apmrouter.byteman.sockets.impl.ISocketImpl, int, int, int)
*/
@Override
public void onSetPerformancePreferences(ISocketImpl socketImpl,
int connectionTime, int latency, int bandwidth) {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#requiresHarvester()
*/
@Override
public boolean requiresHarvester() {
return false;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#stop()
*/
@Override
public synchronized void stop() {
if(!requiresHarvester()) return;
if(keepRunning.get()) {
keepRunning.set(false);
}
if(harvesterThread!=null && harvesterThread.isAlive()) {
harvesterThread.interrupt();
}
harvesterThread = null;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#start()
*/
@Override
public synchronized void start() {
if(!requiresHarvester()) return;
if(!keepRunning.get()) {
keepRunning.set(true);
}
if(harvesterThread!=null && harvesterThread.isAlive()) {
harvesterThread.interrupt();
harvesterThread = null;
}
harvesterThread = newThread(this);
harvesterThread.start();
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#isStarted()
*/
@Override
public boolean isStarted() {
return keepRunning.get();
}
/**
* {@inheritDoc}
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while(keepRunning.get()) {
try {
harvest();
} catch (Throwable t) {
SimpleLogger.error("Harvester Thread Error", t);
} finally {
try {
Thread.currentThread().join(harvesterSleep.get());
} catch (InterruptedException iex) {
Thread.interrupted();
}
}
}
}
/**
* The harvest task implementation
*/
protected void harvest() {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.byteman.sockets.impl.ISocketTracker#hasJMXInterface()
*/
@Override
public boolean hasJMXInterface() {
return false;
}
/**
* Tests a socket's input and output streams to see if the socket is active.
* @param so The socket to test
* @return true if the socket is active, false otherwise
*/
protected boolean testSocketStreams(Socket so) {
if(so==null) return false;
try {
if(!so.isConnected() || so.isClosed()) return false;
boolean ok = testSocketInput(so) && testSocketOutput(so);
if(!ok) return false;
//so.sendUrgentData(0);
NetStat ns = sigar.getNetStat(so.getLocalAddress().getAddress(), so.getLocalPort());
SimpleLogger.info("Local CloseWaits:", ns.getTcpCloseWait());
try {
int cw = sigar.getNetStat(so.getInetAddress().getAddress(), so.getPort()).getTcpCloseWait();
SimpleLogger.info("Remote CloseWaits on [:" + so.getRemoteSocketAddress() + "]", cw);
if(cw>0) return false;
} catch (Exception ex) { SimpleLogger.warn("Failed to get Remote CloseWaits"); }
// if(ns.getTcpCloseWait()>0) {
// return false;
// }
return ok;
} catch (Exception ex) {
if(so.isConnected()) {
try { so.close(); } catch (Exception e) {}
}
return false;
}
}
/**
* Tests the socket's input stream to determine if input is closed
* @param so the socket to test
* @return true if the input is still active, false otherwise
*/
protected boolean testSocketInput(Socket so) {
if(so==null) return false;
try {
if(so.isInputShutdown()) return false;
so.getInputStream().available();
return true;
} catch (Exception ex) {
return false;
}
}
/** An empty byte array buffer constant */
public static final byte[] EMPTY_BYTE_ARR = {};
/** An one byte array buffer constant */
public static final byte[] ONE_BYTE_ARR = {0};
/**
* Tests the socket's output stream to determine if output is closed
* @param so the socket to test
* @return true if the output is still active, false otherwise
*/
protected boolean testSocketOutput(Socket so) {
if(so==null) return false;
try {
if(so.isOutputShutdown()) return false;
OutputStream os = so.getOutputStream();
os.write(ONE_BYTE_ARR, 0, 0);
return true;
} catch (Exception ex) {
SimpleLogger.info("Got exception writing zero bytes of ONE_BYTE_ARR");
return false;
}
}
public static class NetStatPrinter {
/** the netstat to print */
private final NetStat netStat;
/**
* Creates a new NetStatPrinter
* @param remoteNetStat the netstat to print
*/
public NetStatPrinter(NetStat netStat) {
super();
this.netStat = netStat;
}
public int getAllInboundTotal() {
return netStat.getAllInboundTotal();
}
public int getAllOutboundTotal() {
return netStat.getAllOutboundTotal();
}
public int getTcpBound() {
return netStat.getTcpBound();
}
public int getTcpClose() {
return netStat.getTcpClose();
}
public int getTcpCloseWait() {
return netStat.getTcpCloseWait();
}
public int getTcpClosing() {
return netStat.getTcpClosing();
}
public int getTcpEstablished() {
return netStat.getTcpEstablished();
}
public int getTcpFinWait1() {
return netStat.getTcpFinWait1();
}
public int getTcpFinWait2() {
return netStat.getTcpFinWait2();
}
public int getTcpIdle() {
return netStat.getTcpIdle();
}
public int getTcpInboundTotal() {
return netStat.getTcpInboundTotal();
}
public int getTcpLastAck() {
return netStat.getTcpLastAck();
}
public int getTcpListen() {
return netStat.getTcpListen();
}
public int getTcpOutboundTotal() {
return netStat.getTcpOutboundTotal();
}
public int[] getTcpStates() {
return netStat.getTcpStates();
}
public int getTcpSynRecv() {
return netStat.getTcpSynRecv();
}
public int getTcpSynSent() {
return netStat.getTcpSynSent();
}
public int getTcpTimeWait() {
return netStat.getTcpTimeWait();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("NetStatPrinter [\n\tgetAllInboundTotal()=")
.append(getAllInboundTotal())
.append("\n\tgetAllOutboundTotal()=")
.append(getAllOutboundTotal()).append("\n\tgetTcpBound()=")
.append(getTcpBound()).append("\n\tgetTcpClose()=")
.append(getTcpClose()).append("\n\tgetTcpCloseWait()=")
.append(getTcpCloseWait()).append("\n\tgetTcpClosing()=")
.append(getTcpClosing()).append("\n\tgetTcpEstablished()=")
.append(getTcpEstablished()).append("\n\tgetTcpFinWait1()=")
.append(getTcpFinWait1()).append("\n\tgetTcpFinWait2()=")
.append(getTcpFinWait2()).append("\n\tgetTcpIdle()=")
.append(getTcpIdle()).append("\n\tgetTcpInboundTotal()=")
.append(getTcpInboundTotal()).append("\n\tgetTcpLastAck()=")
.append(getTcpLastAck()).append("\n\tgetTcpListen()=")
.append(getTcpListen()).append("\n\tgetTcpOutboundTotal()=")
.append(getTcpOutboundTotal())
.append("\n\tgetTcpSynRecv()=").append(getTcpSynRecv())
.append("\n\tgetTcpSynSent()=").append(getTcpSynSent())
.append("\n\tgetTcpTimeWait()=").append(getTcpTimeWait())
.append("]");
return builder.toString();
}
}
/**
* Returns the name of the current logging level
* @return the current logging level name
*/
public String getLoggingLevel() {
return loggingLevel.name();
}
/**
* Sets the logging level to the passed level name
* @param loggingLevel the level name
*/
public void setLoggingLevel(String loggingLevel) {
this.loggingLevel = Level.forName(loggingLevel);
}
}