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.
474 lines
17 KiB
474 lines
17 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.util.Hashtable;
|
|
import java.util.logging.Level;
|
|
import java.util.Stack;
|
|
import java.net.InetAddress;
|
|
import java.net.SocketException;
|
|
|
|
// jmx imports
|
|
//
|
|
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
|
|
import com.sun.jmx.snmp.SnmpDefinitions;
|
|
import com.sun.jmx.snmp.SnmpStatusException;
|
|
import com.sun.jmx.snmp.SnmpVarBindList;
|
|
|
|
/**
|
|
* This class is used for sending INFORM REQUESTS from an agent to a manager.
|
|
*
|
|
* Creates, controls, and manages one or more inform requests.
|
|
*
|
|
* The SnmpSession maintains the list of all active inform requests and inform responses.
|
|
* Each SnmpSession has a dispatcher that is a thread used to service all the inform requests it creates
|
|
* and each SnmpSession uses a separate socket for sending/receiving inform requests/responses.
|
|
*
|
|
* An SnmpSession object is associated with an SNMP adaptor server.
|
|
* It is created the first time an inform request is sent by the SNMP adaptor server
|
|
* and is destroyed (with its associated SnmpSocket) when the SNMP adaptor server is stopped.
|
|
*
|
|
*/
|
|
|
|
class SnmpSession implements SnmpDefinitions, Runnable {
|
|
|
|
// PRIVATE VARIABLES
|
|
//------------------
|
|
|
|
/**
|
|
* The SNMP adaptor associated with this SnmpSession.
|
|
*/
|
|
protected transient SnmpAdaptorServer adaptor;
|
|
/**
|
|
* The SnmpSocket to be used to communicate with the manager
|
|
* by all inform requests created in this session.
|
|
*/
|
|
protected transient SnmpSocket informSocket = null;
|
|
/**
|
|
* This table maintains the list of inform requests.
|
|
*/
|
|
private transient Hashtable<SnmpInformRequest, SnmpInformRequest> informRequestList =
|
|
new Hashtable<>();
|
|
/**
|
|
* This table maintains the list of inform responses.
|
|
* A FIFO queue is needed here.
|
|
*/
|
|
private transient Stack<SnmpInformRequest> informRespq =
|
|
new Stack<>();
|
|
/**
|
|
* The dispatcher that will service all inform responses to inform requests generated
|
|
* using this session object. An SnmpSession object creates one or more inform requests.
|
|
* Thus it services all inform requests, which are created by this session object,
|
|
* when an inform response arrives for an inform request generated by the session.
|
|
*/
|
|
private transient Thread myThread = null;
|
|
/**
|
|
* Request being synchronized from session thread. This happens when
|
|
* a user does sync operation from a callback.
|
|
*/
|
|
private transient SnmpInformRequest syncInformReq ;
|
|
|
|
SnmpQManager snmpQman = null;
|
|
|
|
private boolean isBeingCancelled = false;
|
|
|
|
// PUBLIC CONSTRUCTORS
|
|
//--------------------
|
|
|
|
/**
|
|
* Constructor for creating a new session.
|
|
* @param adp The SNMP adaptor associated with this SnmpSession.
|
|
* @exception SocketException Unable to initialize the SnmpSocket.
|
|
*/
|
|
public SnmpSession(SnmpAdaptorServer adp) throws SocketException {
|
|
adaptor = adp;
|
|
snmpQman = new SnmpQManager();
|
|
SnmpResponseHandler snmpRespHdlr = new SnmpResponseHandler(adp, snmpQman);
|
|
initialize(adp, snmpRespHdlr);
|
|
}
|
|
/**
|
|
* Constructor for creating a new session. Allows subclassing.
|
|
*/
|
|
public SnmpSession() throws SocketException {
|
|
}
|
|
// OTHER METHODS
|
|
//--------------
|
|
/**
|
|
* Initializes the SnmpSession.
|
|
* @param adp The SNMP adaptor associated with this SnmpSession.
|
|
* @exception SocketException Unable to initialize the SnmpSocket.
|
|
*/
|
|
protected synchronized void initialize(SnmpAdaptorServer adp,
|
|
SnmpResponseHandler snmpRespHdlr)
|
|
throws SocketException {
|
|
informSocket = new SnmpSocket(snmpRespHdlr, adp.getAddress(), adp.getBufferSize().intValue());
|
|
|
|
myThread = new Thread(this, "SnmpSession");
|
|
myThread.start();
|
|
}
|
|
|
|
/**
|
|
* Indicates whether the thread for this session is active and the SNMP adaptor server ONLINE.
|
|
* @return true if active, false otherwise.
|
|
*/
|
|
synchronized boolean isSessionActive() {
|
|
//return ((myThread != null) && (myThread.isAlive()));
|
|
return ((adaptor.isActive()) && (myThread != null) && (myThread.isAlive()));
|
|
}
|
|
|
|
/**
|
|
* Gets the SnmpSocket which will be used by inform requests created in this session.
|
|
* @return The socket which will be used in this session.
|
|
*/
|
|
SnmpSocket getSocket() {
|
|
return informSocket;
|
|
}
|
|
|
|
/**
|
|
* Gets the SnmpQManager which will be used by inform requests created in this session.
|
|
* @return The SnmpQManager which will be used in this session.
|
|
*/
|
|
SnmpQManager getSnmpQManager() {
|
|
return snmpQman;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether this session is performing synchronous operation for an inform request.
|
|
* @return <CODE>true</CODE> if the session is performing synchronous operation, <CODE>false</CODE> otherwise.
|
|
*/
|
|
private synchronized boolean syncInProgress() {
|
|
return syncInformReq != null ;
|
|
}
|
|
|
|
private synchronized void setSyncMode(SnmpInformRequest req) {
|
|
syncInformReq = req ;
|
|
}
|
|
|
|
private synchronized void resetSyncMode() {
|
|
if (syncInformReq == null)
|
|
return ;
|
|
syncInformReq = null ;
|
|
if (thisSessionContext())
|
|
return ;
|
|
this.notifyAll() ;
|
|
}
|
|
|
|
/**
|
|
* Returns <CODE>true</CODE> if the current executing thread is this session's dispatcher.
|
|
* Typically used to detect whether the user is doing a sync operation from
|
|
* this dispatcher context. For instance, a user gives a sync command
|
|
* from within a request callback using its associated session.
|
|
* @return <CODE>true</CODE> if current thread is this session's dispatcher, <CODE>false</CODE> otherwise.
|
|
*/
|
|
boolean thisSessionContext() {
|
|
return (Thread.currentThread() == myThread) ;
|
|
}
|
|
|
|
/**
|
|
* Sends an inform request to the specified InetAddress destination using the specified community string.
|
|
* @param addr The InetAddress destination for this inform request.
|
|
* @param cs The community string to be used for the inform request.
|
|
* @param cb The callback that is invoked when a request is complete.
|
|
* @param vblst A list of SnmpVarBind instances or null.
|
|
* @exception SnmpStatusException SNMP adaptor is not ONLINE or session
|
|
* is dead.
|
|
*/
|
|
SnmpInformRequest makeAsyncRequest(InetAddress addr, String cs,
|
|
SnmpInformHandler cb,
|
|
SnmpVarBindList vblst, int port)
|
|
throws SnmpStatusException {
|
|
|
|
if (!isSessionActive()) {
|
|
throw new SnmpStatusException("SNMP adaptor server not ONLINE");
|
|
}
|
|
SnmpInformRequest snmpreq = new SnmpInformRequest(this, adaptor, addr, cs, port, cb);
|
|
snmpreq.start(vblst);
|
|
return snmpreq;
|
|
}
|
|
|
|
/**
|
|
* Performs sync operations on active requests. Any number of inform requests
|
|
* can be done in sync mode but only one per thread.
|
|
* The user can do synchronous operation using the request handle only.
|
|
*/
|
|
void waitForResponse(SnmpInformRequest req, long waitTime) {
|
|
|
|
if (! req.inProgress())
|
|
return ;
|
|
setSyncMode(req) ;
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"waitForResponse", "Session switching to sync mode for inform request " + req.getRequestId());
|
|
}
|
|
long maxTime ;
|
|
if (waitTime <= 0)
|
|
maxTime = System.currentTimeMillis() + 6000 * 1000 ;
|
|
else
|
|
maxTime = System.currentTimeMillis() + waitTime ;
|
|
|
|
while (req.inProgress() || syncInProgress()) {
|
|
waitTime = maxTime - System.currentTimeMillis() ;
|
|
if (waitTime <= 0)
|
|
break ;
|
|
synchronized (this) {
|
|
if (! informRespq.removeElement(req)) {
|
|
try {
|
|
this.wait(waitTime) ;
|
|
} catch(InterruptedException e) {
|
|
}
|
|
continue ;
|
|
}
|
|
}
|
|
try {
|
|
processResponse(req) ;
|
|
} catch (Exception e) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"waitForResponse", "Got unexpected exception", e);
|
|
}
|
|
}
|
|
}
|
|
resetSyncMode() ;
|
|
}
|
|
|
|
/**
|
|
* Dispatcher method for this session thread. This is the dispatcher method
|
|
* which goes in an endless-loop and waits for servicing inform requests
|
|
* which received a reply from the manager.
|
|
*/
|
|
@Override
|
|
public void run() {
|
|
myThread = Thread.currentThread();
|
|
myThread.setPriority(Thread.NORM_PRIORITY);
|
|
|
|
SnmpInformRequest reqc = null;
|
|
while (myThread != null) {
|
|
try {
|
|
reqc = nextResponse();
|
|
if (reqc != null) {
|
|
processResponse(reqc);
|
|
}
|
|
} catch (ThreadDeath d) {
|
|
myThread = null;
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"run", "ThreadDeath, session thread unexpectedly shutting down");
|
|
}
|
|
throw d ;
|
|
}
|
|
}
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSession.class.getName(),
|
|
"run", "Session thread shutting down");
|
|
}
|
|
myThread = null ;
|
|
}
|
|
|
|
private void processResponse(SnmpInformRequest reqc) {
|
|
|
|
while (reqc != null && myThread != null) {
|
|
try {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSession.class.getName(),
|
|
"processResponse", "Processing response to req = " + reqc.getRequestId());
|
|
}
|
|
reqc.processResponse() ; // Handles out of memory.
|
|
reqc = null ; // finished processing.
|
|
} catch (Exception e) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"processResponse", "Got unexpected exception", e);
|
|
}
|
|
reqc = null ;
|
|
} catch (OutOfMemoryError ome) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"processResponse", "Out of memory error in session thread", ome);
|
|
}
|
|
Thread.yield();
|
|
continue ; // re-process the request.
|
|
}
|
|
}
|
|
}
|
|
|
|
// HANDLING INFORM REQUESTS LIST AND INFORM RESPONSES LIST
|
|
//--------------------------------------------------------
|
|
|
|
/**
|
|
* Adds an inform request.
|
|
* @param snmpreq The inform request to add.
|
|
* @exception SnmpStatusException SNMP adaptor is not ONLINE or session is dead.
|
|
*/
|
|
synchronized void addInformRequest(SnmpInformRequest snmpreq) throws SnmpStatusException {
|
|
|
|
// If the adaptor is not ONLINE, stop adding requests.
|
|
//
|
|
if (!isSessionActive()) {
|
|
throw new SnmpStatusException("SNMP adaptor is not ONLINE or session is dead...") ;
|
|
}
|
|
informRequestList.put(snmpreq, snmpreq);
|
|
}
|
|
|
|
/**
|
|
* Deletes an inform request.
|
|
* @param snmpreq The inform request to delete.
|
|
*/
|
|
synchronized void removeInformRequest(SnmpInformRequest snmpreq) {
|
|
// deleteRequest can be called from destroySnmpSession.
|
|
//In such a case remove is done in cancelAllRequest method.
|
|
if(!isBeingCancelled)
|
|
informRequestList.remove(snmpreq) ;
|
|
|
|
if (syncInformReq != null && syncInformReq == snmpreq) {
|
|
resetSyncMode() ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancels all pending inform requests in this session.
|
|
*/
|
|
private void cancelAllRequests() {
|
|
final SnmpInformRequest[] list;
|
|
|
|
synchronized(this) {
|
|
|
|
if (informRequestList.isEmpty()) {
|
|
return ;
|
|
}
|
|
|
|
isBeingCancelled = true;
|
|
|
|
list = new SnmpInformRequest[informRequestList.size()];
|
|
java.util.Iterator<SnmpInformRequest> it = informRequestList.values().iterator();
|
|
int i = 0;
|
|
while(it.hasNext()) {
|
|
SnmpInformRequest req = it.next();
|
|
list[i++] = req;
|
|
it.remove();
|
|
}
|
|
informRequestList.clear();
|
|
}
|
|
|
|
for(int i = 0; i < list.length; i++)
|
|
list[i].cancelRequest();
|
|
}
|
|
|
|
/**
|
|
* Adds the inform request object which received a response to an inform request
|
|
* generated by the session. This is added to a private store, which
|
|
* will be eventually picked up by the dispatcher for processing.
|
|
* @param reqc The inform request that received the response from the manager.
|
|
*/
|
|
void addResponse(SnmpInformRequest reqc) {
|
|
|
|
SnmpInformRequest snmpreq = reqc;
|
|
if (isSessionActive()) {
|
|
synchronized(this) {
|
|
informRespq.push(reqc) ;
|
|
this.notifyAll() ;
|
|
}
|
|
} else {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSession.class.getName(),
|
|
"addResponse", "Adaptor not ONLINE or session thread dead, so inform response is dropped..." + reqc.getRequestId());
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized SnmpInformRequest nextResponse() {
|
|
|
|
if (informRespq.isEmpty()) {
|
|
try {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSession.class.getName(),
|
|
"nextResponse", "Blocking for response");
|
|
}
|
|
this.wait();
|
|
} catch(InterruptedException e) {
|
|
}
|
|
}
|
|
if (informRespq.isEmpty())
|
|
return null;
|
|
SnmpInformRequest reqc = informRespq.firstElement();
|
|
informRespq.removeElementAt(0) ;
|
|
return reqc ;
|
|
}
|
|
|
|
private synchronized void cancelAllResponses() {
|
|
if (informRespq != null) {
|
|
syncInformReq = null ;
|
|
informRespq.removeAllElements() ;
|
|
this.notifyAll() ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroys any pending inform requests and then stops the session.
|
|
* The session will not be usable after this method returns.
|
|
*/
|
|
final void destroySession() {
|
|
|
|
cancelAllRequests() ;
|
|
cancelAllResponses() ;
|
|
synchronized(this) {
|
|
informSocket.close() ;
|
|
informSocket = null ;
|
|
}
|
|
snmpQman.stopQThreads() ;
|
|
snmpQman = null ;
|
|
killSessionThread() ;
|
|
}
|
|
|
|
/**
|
|
* Make sure you are killing the thread when it is active. Instead
|
|
* prepare for a graceful exit.
|
|
*/
|
|
private synchronized void killSessionThread() {
|
|
|
|
if ((myThread != null) && (myThread.isAlive())) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSession.class.getName(),
|
|
"killSessionThread", "Destroying session");
|
|
}
|
|
if (!thisSessionContext()) {
|
|
myThread = null ;
|
|
this.notifyAll() ;
|
|
} else
|
|
myThread = null ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalizer of the <CODE>SnmpSession</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>Removes all the requests for this SNMP session, closes the socket and
|
|
* sets all the references to the <CODE>SnmpSession</CODE> object to <CODE>null</CODE>.
|
|
*/
|
|
@Override
|
|
protected void finalize() {
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSession.class.getName(),
|
|
"finalize", "Shutting all servers");
|
|
}
|
|
|
|
if (informRespq != null)
|
|
informRespq.removeAllElements() ;
|
|
informRespq = null ;
|
|
if (informSocket != null)
|
|
informSocket.close() ;
|
|
informSocket = null ;
|
|
|
|
snmpQman = null ;
|
|
}
|
|
|
|
}
|