package net.johnewart.gearman.net; import com.google.common.primitives.Ints; import net.johnewart.gearman.common.packets.Packet; import net.johnewart.gearman.common.packets.PacketFactory; import net.johnewart.gearman.common.packets.request.EchoRequest; import net.johnewart.gearman.common.packets.response.EchoResponse; import net.johnewart.gearman.constants.GearmanConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.util.Arrays; import java.util.Date; public class Connection { protected Socket socket; protected String hostname; protected int port; private Logger LOG = LoggerFactory.getLogger(Connection.class); private Long lastTimeSeenAlive; private boolean isGood; private long HEALTHCHECK_MSEC = 1800 * 1000; // 1800 sec in msec (30 min) public Connection() { } public Connection(String hostname, int port) { this.hostname = hostname; this.port = port; } public Connection(Socket socket) { this.socket = socket; } public Connection(Connection c) { this.hostname = c.hostname; this.port = c.port; } public void sendPacket(Packet p) throws IOException { try { initializeConnection(); socket.getOutputStream().write(p.toByteArray()); } catch (IOException ioe) { isGood = false; throw ioe; } } public void close() throws IOException { socket.close(); } public String toString() { return String.format("%s:%d", this.hostname, this.port); } public boolean isHealthy() { try { initializeConnection(); } catch(IOException ioe) { return false; } // TODO: make this a little smarter. if(isGood && !shouldCheckHealth()) { return true; } else { try { this.sendPacket(new EchoRequest("OK")); EchoResponse response = (EchoResponse)(this.getNextPacket()); if(response != null) { byte[] data = response.getData(); byte[] matchData = "OK".getBytes(GearmanConstants.CHARSET); if(Arrays.equals(data, matchData)) { this.updateLastTimeSeenAlive(); return true; } } } catch (IOException ioe) { LOG.error("Client unable to write to socket: " + ioe.toString()); try { this.socket.close(); } catch (IOException closeException) { LOG.error("Unable to close dead socket: " + closeException.toString()); } } return false; } } public Packet getNextPacket() throws IOException { return getNextPacket(0); } public Packet getNextPacket(int socketTimeout) throws IOException { try { initializeConnection(); } catch (IOException ioe) { this.isGood = false; return null; } int messagesize = -1; // Initialize to 12 bytes (header only), and resize later as needed byte[] header = new byte[12]; byte[] packetBytes; try { socket.setSoTimeout(socketTimeout); InputStream is = socket.getInputStream(); int numbytes = is.read(header, 0, 12); if(numbytes == 12) { // Check byte count byte[] sizebytes = Arrays.copyOfRange(header, 8, 12); messagesize = Ints.fromByteArray(sizebytes); if (messagesize > 0) { // Grow packet buffer to fit data packetBytes = Arrays.copyOf(header, 12 + messagesize); } else { packetBytes = header; } is.read(packetBytes, 12, messagesize); return PacketFactory.packetFromBytes(packetBytes); } else if(numbytes == -1) { throw new IOException("Network socket EOF."); } } catch (IOException ioe) { LOG.error("Exception reading data: ", ioe.toString()); throw ioe; } return null; } public Long getLastTimeSeenAlive() { if(lastTimeSeenAlive != null) return lastTimeSeenAlive; else return 0L; } public void setLastTimeSeenAlive(Long lastTimeSeenAlive) { this.lastTimeSeenAlive = lastTimeSeenAlive; } private boolean shouldCheckHealth() { Long now = new Date().getTime(); if(now - getLastTimeSeenAlive() > HEALTHCHECK_MSEC) { return true; } else { return false; } } private void initializeConnection() throws IOException { if(socket == null || socket.isClosed()) { socket = new Socket(hostname, port); this.isGood = true; } } public void updateLastTimeSeenAlive() { this.lastTimeSeenAlive = new Date().getTime(); } public void setHealthCheckInterval(long interval) { this.HEALTHCHECK_MSEC = interval; } }