/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.icmp.jni6; import java.io.IOException; import java.net.DatagramPacket; import java.net.Inet6Address; import java.net.NoRouteToHostException; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.icmp.EchoPacket; import org.opennms.netmgt.icmp.HostIsDownException; import org.opennms.netmgt.icmp.PingResponseCallback; import org.opennms.protocols.icmp6.ICMPv6EchoRequest; import org.opennms.protocols.icmp6.ICMPv6Packet.Type; import org.opennms.protocols.icmp6.ICMPv6Socket; import org.opennms.protocols.rt.Request; /** * This class is used to encapsulate a ping request. A request consist of * the pingable address and a signaled state. * * @author <a href="mailto:ranger@opennms.org">Ben Reed</a> * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> */ public class Jni6PingRequest implements Request<Jni6PingRequestId, Jni6PingRequest, Jni6PingResponse>, EchoPacket { private static long s_nextTid = 1; public static synchronized final long getNextTID() { return s_nextTid++; } /** * The id representing the packet */ private final Jni6PingRequestId m_id; /** * the request packet */ private ICMPv6EchoRequest m_requestPacket = null; /** * The callback to use when this object is ready to do something */ private final PingResponseCallback m_callback; /** * How many retries */ private final int m_retries; /** * how long to wait for a response */ private final long m_timeout; /** * The ICMP packet size */ private final int m_packetsize; /** * The expiration time of this request */ private long m_expiration = -1L; /** * The thread logger associated with this request. */ private final ThreadCategory m_log; private final AtomicBoolean m_processed = new AtomicBoolean(false); public Jni6PingRequest(Jni6PingRequestId id, long timeout, int retries, int packetsize, ThreadCategory log, PingResponseCallback callback) { m_id = id; m_timeout = timeout; m_retries = retries; m_packetsize = packetsize; m_log = log; m_callback = callback; } public Jni6PingRequest(Inet6Address addr, int identifier, int sequenceNumber, long threadId, long timeout, int retries, int packetsize, ThreadCategory logger, PingResponseCallback cb) { this(new Jni6PingRequestId(addr, identifier, sequenceNumber, threadId), timeout, retries, packetsize, logger, cb); } public Jni6PingRequest(Inet6Address addr, int identifier, int sequenceNumber, long threadId, long timeout, int retries, int packetsize, PingResponseCallback cb) { this(addr, identifier, sequenceNumber, threadId, timeout, retries, packetsize, ThreadCategory.getInstance(Jni6PingRequest.class), cb); } public Jni6PingRequest(Inet6Address addr, int identifier, int sequenceNumber, long timeout, int retries, int packetsize, PingResponseCallback cb) { this(addr, identifier, sequenceNumber, getNextTID(), timeout, retries, packetsize, cb); } /** * <p>processResponse</p> * * @param reply a {@link org.opennms.netmgt.icmp.Jni6PingResponse.JniPingResponse.PingReply} object. * @return a boolean. */ public boolean processResponse(Jni6PingResponse reply) { try { m_log.debug(System.currentTimeMillis()+": Ping Response Received "+this); m_callback.handleResponse(m_id.getAddress(), reply); } finally { setProcessed(true); } return true; } /** * <p>processTimeout</p> * * @return a {@link org.opennms.netmgt.icmp.jni6.Jni6PingRequest} object. */ public Jni6PingRequest processTimeout() { try { Jni6PingRequest returnval = null; if (this.isExpired()) { if (m_retries > 0) { returnval = new Jni6PingRequest(m_id, m_timeout, (m_retries - 1), m_packetsize, m_log, m_callback); m_log.debug(System.currentTimeMillis()+": Retrying Ping Request "+returnval); } else { m_log.debug(System.currentTimeMillis()+": Ping Request Timed out "+this); m_callback.handleTimeout(m_id.getAddress(), this); } } return returnval; } finally { setProcessed(true); } } /** * <p>isExpired</p> * * @return a boolean. */ public boolean isExpired() { return (System.currentTimeMillis() >= m_expiration); } /** * <p>toString</p> * * @return a {@link java.lang.String} object. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); sb.append("ID=").append(m_id).append(','); sb.append("Retries=").append(m_retries).append(","); sb.append("Timeout=").append(m_timeout).append(","); sb.append("Packet-Size=").append(m_packetsize).append(","); sb.append("Expiration=").append(m_expiration).append(','); sb.append("Callback=").append(m_callback); sb.append("]"); return sb.toString(); } /** {@inheritDoc} */ public long getDelay(TimeUnit unit) { return unit.convert(m_expiration - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } /** * <p>compareTo</p> * * @param request a {@link java.util.concurrent.Delayed} object. * @return a int. */ public int compareTo(Delayed request) { long myDelay = getDelay(TimeUnit.MILLISECONDS); long otherDelay = request.getDelay(TimeUnit.MILLISECONDS); if (myDelay < otherDelay) return -1; if (myDelay == otherDelay) return 0; return 1; } /** * <p>getId</p> * * @return a {@link org.opennms.netmgt.icmp.Jni6PingRequestId.JniPingRequestId.PingRequestId} object. */ public Jni6PingRequestId getId() { return m_id; } public void processError(Throwable t) { try { m_callback.handleError(m_id.getAddress(), this, t); } finally { setProcessed(true); } } private void setProcessed(boolean processed) { m_processed.set(processed); } /** * <p>isProcessed</p> * * @return a boolean. */ public boolean isProcessed() { return m_processed.get(); } /** * Send this JniPingRequest through the given icmpSocket * * @param socket a {@link org.opennms.protocols.icmp.IcmpSocket} object. */ public void send(ICMPv6Socket socket) { try { m_requestPacket = createRequestPacket(); m_log.debug(System.currentTimeMillis()+": Sending Ping Request: "+this); byte[] data = m_requestPacket.toBytes(); m_expiration = System.currentTimeMillis() + m_timeout; send(socket, new DatagramPacket(data, data.length, m_id.getAddress(), 0)); } catch (Throwable t) { m_callback.handleError(m_id.getAddress(), this, t); } } private void send(ICMPv6Socket socket, DatagramPacket packet) throws IOException { try { socket.send(packet); } catch(IOException e) { if (e.getMessage().matches("sendto error \\(65, .*\\)")) { throw new NoRouteToHostException("No Route to Host " + m_id.getAddress() + ": " + e.getMessage()); } else if (e.getMessage().matches("sendto error \\(64, .*\\)")) { throw new HostIsDownException("Host " + m_id.getAddress() + " is down: " + e.getMessage()); } else { throw e; } } } private ICMPv6EchoRequest getRequestPacket() { return m_requestPacket; } private ICMPv6EchoRequest createRequestPacket() { return new ICMPv6EchoRequest(m_id.getIdentifier(), m_id.getSequenceNumber(), m_id.getThreadId(), m_packetsize); } @Override public boolean isEchoReply() { return getRequestPacket().getType() == Type.EchoReply; } @Override public int getIdentifier() { return getRequestPacket().getIdentifier(); } @Override public int getSequenceNumber() { return getRequestPacket().getSequenceNumber(); } @Override public long getThreadId() { return getRequestPacket().getThreadId(); } @Override public long getReceivedTimeNanos() { return getRequestPacket().getReceiveTime() * 1000000; } @Override public long getSentTimeNanos() { return getRequestPacket().getSentTime() * 1000000; } @Override public double elapsedTime(TimeUnit timeUnit) { // {@link org.opennms.protocols.icmp.ICMPEchoPacket.getPingRTT()} returns microseconds. double nanosPerUnit = TimeUnit.NANOSECONDS.convert(1, timeUnit); return (getRequestPacket().getRoundTripTime() * 1000) / nanosPerUnit; } }