You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
11 KiB
299 lines
11 KiB
/*
|
|
*
|
|
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
// Copyright (c) 1995-96 by Cisco Systems, Inc.
|
|
|
|
package com.sun.jmx.snmp.daemon;
|
|
|
|
// JAVA imports
|
|
//
|
|
import java.net.InetAddress;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.SocketException;
|
|
import java.io.IOException;
|
|
import java.util.logging.Level;
|
|
|
|
// SNMP Runtime imports
|
|
//
|
|
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
|
|
|
|
/**
|
|
* This class creates an SNMP Datagram Socket. This class has methods helpful
|
|
* to send SNMP inform request packets to an arbitrary port of a specified device.
|
|
* It also runs a thread that is devoted to receiving SNMP inform response on the socket.
|
|
* <BR>
|
|
* A socket imposes an upper limit on size of inform response packet. Any
|
|
* packet which exceeds this limit is truncated. By default, this
|
|
* limit is {@link SnmpAdaptorServer#bufferSize}.
|
|
*/
|
|
|
|
final class SnmpSocket implements java.lang.Runnable {
|
|
|
|
|
|
// VARIABLES
|
|
//----------
|
|
|
|
private DatagramSocket _socket = null;
|
|
private SnmpResponseHandler _dgramHdlr = null;
|
|
private Thread _sockThread = null;
|
|
private byte[] _buffer = null;
|
|
private transient boolean isClosing = false;
|
|
|
|
int _socketPort = 0;
|
|
int responseBufSize = 1024;
|
|
|
|
// CONSTRUCTORS
|
|
//-------------
|
|
|
|
/**
|
|
* Creates a new <CODE>SnmpSocket</CODE> object.
|
|
* @param rspHdlr A Datagram handler.
|
|
* @param bufferSize The SNMP adaptor buffer size.
|
|
* @exception SocketException A socket could not be created.
|
|
*/
|
|
public SnmpSocket(SnmpResponseHandler rspHdlr, InetAddress addr, int bufferSize) throws SocketException {
|
|
super();
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"constructor", "Creating new SNMP datagram socket");
|
|
}
|
|
|
|
// TIME BOMB HERE
|
|
_socket = new DatagramSocket(0, addr);
|
|
_socketPort = _socket.getLocalPort();
|
|
responseBufSize = bufferSize;
|
|
_buffer = new byte[responseBufSize];
|
|
_dgramHdlr = rspHdlr;
|
|
_sockThread = new Thread(this, "SnmpSocket");
|
|
_sockThread.start();
|
|
}
|
|
|
|
// PUBLIC METHODS
|
|
//---------------
|
|
|
|
/**
|
|
* Sends a datagram packet to a specified device at specified port.
|
|
* @param buff The packet data.
|
|
* @param length The packet length.
|
|
* @param addr The destination address.
|
|
* @param port The destination port number.
|
|
* @exception IOException Signals that an I/O exception of some sort has occurred.
|
|
*/
|
|
public synchronized void sendPacket(byte[] buff, int length, InetAddress addr, int port) throws IOException {
|
|
DatagramPacket dgrmpkt;
|
|
dgrmpkt = new DatagramPacket(buff, length, addr, port);
|
|
sendPacket(dgrmpkt);
|
|
}
|
|
|
|
/**
|
|
* Sends a datagram packet to a specified device at specified port.
|
|
* @param dgrmpkt The datagram packet.
|
|
* @exception IOException Signals that an I/O exception of some sort has occurred.
|
|
*/
|
|
public synchronized void sendPacket(DatagramPacket dgrmpkt) throws IOException {
|
|
|
|
try {
|
|
if (isValid()) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"sendPacket", "Sending DatagramPacket. Length = " + dgrmpkt.getLength() +
|
|
" through socket = " + _socket.toString());
|
|
}
|
|
_socket.send(dgrmpkt);
|
|
} else
|
|
throw new IOException("Invalid state of SNMP datagram socket.");
|
|
} catch (IOException e) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"sendPacket", "I/O error while sending", e);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the socket is initialised correctly and if it is still active.
|
|
* @return <CODE>true</CODE> if the socket is initialised correctly and if it is still active,
|
|
* <CODE>false</CODE> otherwise.
|
|
*/
|
|
public synchronized boolean isValid() {
|
|
return _socket != null && _sockThread != null && _sockThread.isAlive();
|
|
}
|
|
|
|
/**
|
|
* Closes the socket and its associated resources.
|
|
*/
|
|
public synchronized void close() {
|
|
|
|
isClosing = true;
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"close", "Closing and destroying the SNMP datagram socket -> " + toString());
|
|
}
|
|
|
|
try {
|
|
// We send an empty datagram packet to fix bug 4293791 (it's a jdk 1.1 bug)
|
|
//
|
|
DatagramSocket sn = new java.net.DatagramSocket(0);
|
|
byte[] ob = new byte[1];
|
|
DatagramPacket pk = new DatagramPacket(ob , 1, java.net.InetAddress.getLocalHost(), _socketPort);
|
|
sn.send(pk);
|
|
sn.close();
|
|
} catch (Exception e) {}
|
|
|
|
// First close the datagram socket.
|
|
// This may generates an IO exception at the run method (_socket.receive).
|
|
//
|
|
if (_socket != null) {
|
|
_socket.close() ;
|
|
_socket = null ;
|
|
}
|
|
|
|
// Then stop the thread socket.
|
|
//
|
|
if (_sockThread != null && _sockThread.isAlive()) {
|
|
_sockThread.interrupt();
|
|
try {
|
|
// Wait until the thread die.
|
|
//
|
|
_sockThread.join();
|
|
} catch (InterruptedException e) {
|
|
// Ignore...
|
|
}
|
|
_sockThread = null ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dispatcher method for this socket thread. This is the dispatcher method
|
|
* which goes in an endless-loop and waits for receiving datagram packets on the socket.
|
|
*/
|
|
@Override
|
|
public void run() {
|
|
Thread.currentThread().setPriority(8);
|
|
|
|
while (true) {
|
|
try {
|
|
DatagramPacket dgrmpkt = new DatagramPacket (_buffer, _buffer.length);
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"run", "[" + Thread.currentThread().toString() + "]:" + "Blocking for receiving packet");
|
|
}
|
|
|
|
_socket.receive(dgrmpkt);
|
|
|
|
// If the corresponding session is being destroyed, stop handling received responses.
|
|
//
|
|
if (isClosing)
|
|
break;
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"run", "[" + Thread.currentThread().toString() + "]:" + "Received a packet");
|
|
}
|
|
|
|
if (dgrmpkt.getLength() <= 0)
|
|
continue;
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSocket.class.getName(),
|
|
"run", "[" + Thread.currentThread().toString() + "]:" + "Received a packet from : " +
|
|
dgrmpkt.getAddress().toString() + ", Length = " + dgrmpkt.getLength());
|
|
}
|
|
|
|
handleDatagram(dgrmpkt);
|
|
|
|
// We are closing the snmp socket while handling the datagram.
|
|
//
|
|
if (isClosing)
|
|
break;
|
|
|
|
} catch (IOException io) {
|
|
// If the IO exception has been generated because of closing this SNMP socket,
|
|
// (call to _socket.close while _socket is blocked for receiving packet) simply terminate closing properly.
|
|
//
|
|
if (isClosing) {
|
|
break;
|
|
}
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"run", "IOEXception while receiving datagram", io);
|
|
}
|
|
} catch (Exception e) {
|
|
// If the exception (NullPointerException) has been generated because of closing this SNMP socket,
|
|
// (call to _socket = null while _socket is blocked for receiving packet) simply terminate closing properly.
|
|
//
|
|
if (isClosing) {
|
|
break;
|
|
}
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"run", "Exception in socket thread...", e);
|
|
}
|
|
} catch (ThreadDeath d) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"run", "Socket Thread DEAD..." + toString(), d);
|
|
}
|
|
close();
|
|
throw d; // rethrow dead thread.
|
|
} catch (Error err) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"run", "Got unexpected error", err);
|
|
}
|
|
handleJavaError(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalizer of the <CODE>SnmpSocket</CODE> objects.
|
|
* This method is called by the garbage collector on an object
|
|
* when garbage collection determines that there are no more references to the object.
|
|
* <P>Closes the datagram socket and stops the socket thread associated to this SNMP socket.
|
|
*/
|
|
@Override
|
|
protected synchronized void finalize() {
|
|
close();
|
|
}
|
|
|
|
// PRIVATE METHODS
|
|
//----------------
|
|
|
|
/*
|
|
* Keep this locked so that send can't happen.
|
|
*/
|
|
private synchronized void handleJavaError(Throwable thr) {
|
|
if (thr instanceof OutOfMemoryError) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"handleJavaError", "OutOfMemory error", thr);
|
|
}
|
|
Thread.yield();
|
|
return ;
|
|
}
|
|
if (_socket != null) {
|
|
_socket.close();
|
|
_socket = null;
|
|
}
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSocket.class.getName(),
|
|
"handleJavaError", "Global Internal error");
|
|
}
|
|
Thread.yield();
|
|
}
|
|
|
|
private synchronized void handleDatagram(DatagramPacket dgrmpkt) {
|
|
_dgramHdlr.processDatagram(dgrmpkt);
|
|
}
|
|
|
|
}
|