/*******************************************************************************
* 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.jna;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
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.LogPrefixPreservingPingResponseCallback;
import org.opennms.netmgt.icmp.PingResponseCallback;
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 JnaPingRequest implements Request<JnaPingRequestId, JnaPingRequest, JnaPingReply>, EchoPacket {
private static long s_nextTid = 1;
public static synchronized final long getNextTID() {
return s_nextTid++;
}
/**
* The id representing the packet
*/
private final JnaPingRequestId m_id;
/**
* 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 including the header
*/
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 JnaPingRequest(final JnaPingRequestId id, final long timeout, final int retries, final int packetsize, final ThreadCategory log, final PingResponseCallback cb) {
m_id = id;
m_retries = retries;
m_packetsize = packetsize;
m_timeout = timeout;
m_log = log;
m_callback = new LogPrefixPreservingPingResponseCallback(cb);
}
public JnaPingRequest(final InetAddress addr, final int identifier, final int sequenceId, final long threadId, final long timeout, final int retries, final int packetsize, final PingResponseCallback cb) {
this(new JnaPingRequestId(addr, identifier, sequenceId, threadId), timeout, retries, packetsize, ThreadCategory.getInstance(JnaPingRequest.class), cb);
}
public JnaPingRequest(final InetAddress addr, final int identifier, final int sequenceId, final long timeout, final int retries, final int packetsize, final PingResponseCallback cb) {
this(addr, identifier, sequenceId, getNextTID(), timeout, retries, packetsize, cb);
}
/**
* <p>processResponse</p>
*
* @param reply a {@link org.opennms.netmgt.icmp.spi.JnaPingReply.PingReply} object.
* @return a boolean.
*/
public boolean processResponse(final JnaPingReply reply) {
try {
m_log.debug(System.currentTimeMillis()+": Ping Response Received for request: "+this);
m_callback.handleResponse(getAddress(), reply);
} finally {
setProcessed(true);
}
return true;
}
/**
* <p>processTimeout</p>
*
* @return a {@link org.opennms.netmgt.JnaPingRequest.AbstractPingRequest} object.
*/
public JnaPingRequest processTimeout() {
try {
JnaPingRequest returnval = null;
if (this.isExpired()) {
if (m_retries > 0) {
returnval = new JnaPingRequest(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(getAddress(), this);
}
}
return returnval;
} finally {
setProcessed(true);
}
}
/**
* <p>isExpired</p>
*
* @return a boolean.
*/
public boolean isExpired() {
return (System.currentTimeMillis() >= m_expiration);
}
/** {@inheritDoc} */
public long getDelay(final 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(final Delayed request) {
final long myDelay = getDelay(TimeUnit.MILLISECONDS);
final 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.spi.JnaPingRequestId.PingRequestId} object.
*/
public JnaPingRequestId getId() {
return m_id;
}
/** {@inheritDoc} */
public void processError(final Throwable t) {
try {
m_callback.handleError(getAddress(), this, t);
} finally {
setProcessed(true);
}
}
private void setProcessed(final boolean processed) {
m_processed.set(processed);
}
/**
* <p>isProcessed</p>
*
* @return a boolean.
*/
public boolean isProcessed() {
return m_processed.get();
}
/**
* Send this V4PingRequest through the given icmpSocket
* @param icmpSocket a {@link org.opennms.protocols.icmp.IcmpSocket} object.
*/
public void send(final V4Pinger v4, final V6Pinger v6) {
InetAddress addr = getAddress();
if (addr instanceof Inet4Address) {
send(v4, (Inet4Address)addr);
} else if (addr instanceof Inet6Address) {
send(v6, (Inet6Address)addr);
}
}
public InetAddress getAddress() {
return m_id.getAddress();
}
public void send(final V6Pinger v6, final Inet6Address addr6) {
try {
//throw new IllegalStateException("The m_request field should be set here!!!");
m_log.debug(System.currentTimeMillis()+": Sending Ping Request: " + this);
m_expiration = System.currentTimeMillis() + m_timeout;
v6.ping(addr6, m_id.getIdentifier(), m_id.getSequenceNumber(), m_id.getThreadId(), 1, 0, m_packetsize);
} catch (final Throwable t) {
m_callback.handleError(getAddress(), this, t);
}
}
public void send(final V4Pinger v4, final Inet4Address addr4) {
try {
//throw new IllegalStateException("The m_request field should be set here!!!");
m_log.debug(System.currentTimeMillis()+": Sending Ping Request: " + this);
m_expiration = System.currentTimeMillis() + m_timeout;
v4.ping(addr4, m_id.getIdentifier(), m_id.getSequenceNumber(), m_id.getThreadId(), 1, 0, m_packetsize);
} catch (final Throwable t) {
m_callback.handleError(getAddress(), this, t);
}
}
/**
* <p>toString</p>
*
* @return a {@link java.lang.String} object.
*/
@Override
public String toString() {
final 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();
}
@Override
public boolean isEchoReply() {
return false;
}
@Override
public int getIdentifier() {
return m_id.getIdentifier();
}
@Override
public int getSequenceNumber() {
return m_id.getSequenceNumber();
}
@Override
public long getThreadId() {
return m_id.getThreadId();
}
@Override
public long getReceivedTimeNanos() {
throw new UnsupportedOperationException("EchoPacket.getReceivedTimeNanos is not yet implemented");
}
@Override
public long getSentTimeNanos() {
throw new UnsupportedOperationException("EchoPacket.getSentTimeNanos is not yet implemented");
}
@Override
public double elapsedTime(TimeUnit timeUnit) {
throw new UnsupportedOperationException("EchoPacket.elapsedTime is not yet implemented");
}
}