/*
* Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jmx.snmp.daemon;
// java import
//
import java.io.ObjectInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.Vector;
import java.util.NoSuchElementException;
// jmx import
//
import javax.management.MBeanServer;
import javax.management.MBeanRegistration;
import javax.management.ObjectName;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanNotificationInfo;
import javax.management.AttributeChangeNotification;
import javax.management.ListenerNotFoundException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.MBeanServerFactory;
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
// JSR 160 import
//
// XXX Revisit:
// used to import com.sun.jmx.snmp.MBeanServerForwarder
// Now using JSR 160 instead. => this is an additional
// dependency to JSR 160.
//
import javax.management.remote.MBeanServerForwarder;
/**
* Defines generic behavior for the server part of a connector or an adaptor.
* Most connectors or adaptors extend CommunicatorServer
* and inherit this behavior. Connectors or adaptors that do not fit into
* this model do not extend CommunicatorServer
.
*
* A CommunicatorServer
is an active object, it listens for
* client requests and processes them in its own thread. When necessary, a
* CommunicatorServer
creates other threads to process multiple
* requests concurrently.
*
* A CommunicatorServer
object can be stopped by calling the
* stop
method. When it is stopped, the
* CommunicatorServer
no longer listens to client requests and
* no longer holds any thread or communication resources.
* It can be started again by calling the start
method.
*
* A CommunicatorServer
has a State
attribute
* which reflects its activity.
*
*
CommunicatorServer | State |
---|---|
stopped | OFFLINE |
starting | STARTING |
running | ONLINE |
stopping | STOPPING |
* The STARTING
state marks the transition
* from OFFLINE
to ONLINE
.
*
* The STOPPING
state marks the transition from
* ONLINE
to OFFLINE
. This occurs when the
* CommunicatorServer
is finishing or interrupting active
* requests.
*
* When a CommunicatorServer
is unregistered from the MBeanServer,
* it is stopped automatically.
*
* When the value of the State
attribute changes the
* CommunicatorServer
sends a
* {@link javax.management.AttributeChangeNotification} to the
* registered listeners, if any.
*
*
This API is a Sun Microsystems internal API and is subject * to change without notice.
*/ public abstract class CommunicatorServer implements Runnable, MBeanRegistration, NotificationBroadcaster, CommunicatorServerMBean { // // States of a CommunicatorServer // /** * Represents anONLINE
state.
*/
public static final int ONLINE = 0 ;
/**
* Represents an OFFLINE
state.
*/
public static final int OFFLINE = 1 ;
/**
* Represents a STOPPING
state.
*/
public static final int STOPPING = 2 ;
/**
* Represents a STARTING
state.
*/
public static final int STARTING = 3 ;
//
// Types of connectors.
//
/**
* Indicates that it is an RMI connector type.
*/
//public static final int RMI_TYPE = 1 ;
/**
* Indicates that it is an HTTP connector type.
*/
//public static final int HTTP_TYPE = 2 ;
/**
* Indicates that it is an HTML connector type.
*/
//public static final int HTML_TYPE = 3 ;
/**
* Indicates that it is an SNMP connector type.
*/
public static final int SNMP_TYPE = 4 ;
/**
* Indicates that it is an HTTPS connector type.
*/
//public static final int HTTPS_TYPE = 5 ;
//
// Package variables
//
/**
* The state of the connector server.
*/
transient volatile int state = OFFLINE ;
/**
* The object name of the connector server.
* @serial
*/
ObjectName objectName ;
MBeanServer topMBS;
MBeanServer bottomMBS;
/**
*/
transient String dbgTag = null ;
/**
* The maximum number of clients that the CommunicatorServer can
* process concurrently.
* @serial
*/
int maxActiveClientCount = 1 ;
/**
*/
transient int servedClientCount = 0 ;
/**
* The host name used by this CommunicatorServer.
* @serial
*/
String host = null ;
/**
* The port number used by this CommunicatorServer.
* @serial
*/
int port = -1 ;
//
// Private fields
//
/* This object controls access to the "state" and "interrupted" variables.
If held at the same time as the lock on "this", the "this" lock must
be taken first. */
private transient Object stateLock = new Object();
private transient VectorCommunicatorServer
.
*
* @param connectorType Indicates the connector type. Possible values are:
* SNMP_TYPE.
*
* @exception java.lang.IllegalArgumentException
* This connector type is not correct.
*/
public CommunicatorServer(int connectorType)
throws IllegalArgumentException {
switch (connectorType) {
case SNMP_TYPE :
//No op. int Type deciding debugging removed.
break;
default:
throw new IllegalArgumentException("Invalid connector Type") ;
}
dbgTag = makeDebugTag() ;
}
protected Thread createMainThread() {
return new Thread (this, makeThreadName());
}
/**
* Starts this CommunicatorServer
.
*
* Has no effect if this CommunicatorServer
is
* ONLINE
or STOPPING
.
* @param timeout Time in ms to wait for the connector to start.
* If timeout
is positive, wait for at most
* the specified time. An infinite timeout can be specified
* by passing a timeout
value equals
* Long.MAX_VALUE
. In that case the method
* will wait until the connector starts or fails to start.
* If timeout is negative or zero, returns as soon as possible
* without waiting.
* @exception CommunicationException if the connectors fails to start.
* @exception InterruptedException if the thread is interrupted or the
* timeout expires.
*/
public void start(long timeout)
throws CommunicationException, InterruptedException {
boolean start;
synchronized (stateLock) {
if (state == STOPPING) {
// Fix for bug 4352451:
// "java.net.BindException: Address in use".
waitState(OFFLINE, 60000);
}
start = (state == OFFLINE);
if (start) {
changeState(STARTING);
stopRequested = false;
interrupted = false;
startException = null;
}
}
if (!start) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"start","Connector is not OFFLINE");
}
return;
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"start","--> Start connector ");
}
mainThread = createMainThread();
mainThread.start() ;
if (timeout > 0) waitForStart(timeout);
}
/**
* Starts this CommunicatorServer
.
*
* Has no effect if this CommunicatorServer
is
* ONLINE
or STOPPING
.
*/
public void start() {
try {
start(0);
} catch (InterruptedException x) {
// cannot happen because of `0'
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"start","interrupted", x);
}
}
}
/**
* Stops this CommunicatorServer
.
*
* Has no effect if this CommunicatorServer
is
* OFFLINE
or STOPPING
.
*/
public void stop() {
synchronized (stateLock) {
if (state == OFFLINE || state == STOPPING) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"stop","Connector is not ONLINE");
}
return;
}
changeState(STOPPING);
//
// Stop the connector thread
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"stop","Interrupt main thread");
}
stopRequested = true ;
if (!interrupted) {
interrupted = true;
mainThread.interrupt();
}
}
//
// Call terminate on each active client handler
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"stop","terminateAllClient");
}
terminateAllClient() ;
// ----------------------
// changeState
// ----------------------
synchronized (stateLock) {
if (state == STARTING)
changeState(OFFLINE);
}
}
/**
* Tests whether the CommunicatorServer
is active.
*
* @return True if connector is ONLINE
; false otherwise.
*/
public boolean isActive() {
synchronized (stateLock) {
return (state == ONLINE);
}
}
/**
*
Waits until either the State attribute of this MBean equals the
* specified wantedState parameter,
* or the specified timeOut has elapsed.
* The method waitState
returns with a boolean value
* indicating whether the specified wantedState parameter
* equals the value of this MBean's State attribute at the time the method
* terminates.
Two special cases for the timeOut parameter value are:
*waitState
* returns immediately (i.e. does not wait at all),waitState
* waits untill the value of this MBean's State attribute
* is the same as the wantedState parameter (i.e. will wait
* indefinitely if this condition is never met).CommunicatorServer.OFFLINE
,CommunicatorServer.ONLINE
,CommunicatorServer.STARTING
,CommunicatorServer.STOPPING
.Waits until the communicator is started or timeout expires.
*
* @param timeout Time in ms to wait for the connector to start.
* If timeout
is positive, wait for at most
* the specified time. An infinite timeout can be specified
* by passing a timeout
value equals
* Long.MAX_VALUE
. In that case the method
* will wait until the connector starts or fails to start.
* If timeout is negative or zero, returns as soon as possible
* without waiting.
*
* @exception CommunicationException if the connectors fails to start.
* @exception InterruptedException if the thread is interrupted or the
* timeout expires.
*
*/
private void waitForStart(long timeout)
throws CommunicationException, InterruptedException {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitForStart", "Timeout=" + timeout +
" ; current state = " + getStateString());
}
final long startTime = System.currentTimeMillis();
synchronized (stateLock) {
while (state == STARTING) {
// Time elapsed since startTime...
//
final long elapsed = System.currentTimeMillis() - startTime;
// wait for timeout - elapsed.
// A timeout of Long.MAX_VALUE is equivalent to something
// like 292271023 years - which is pretty close to
// forever as far as we are concerned ;-)
//
final long remainingTime = timeout-elapsed;
// If remainingTime is negative, the timeout has elapsed.
//
if (remainingTime < 0) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitForStart", "timeout < 0, return without wait");
}
throw new InterruptedException("Timeout expired");
}
// We're going to wait until someone notifies on the
// the stateLock object, or until the timeout expires,
// or until the thread is interrupted.
//
try {
stateLock.wait(remainingTime);
} catch (InterruptedException e) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitForStart", "wait interrupted");
}
// If we are now ONLINE, then no need to rethrow the
// exception... we're simply going to exit the while
// loop. Otherwise, throw the InterruptedException.
//
if (state != ONLINE) throw e;
}
}
// We're no longer in STARTING state
//
if (state == ONLINE) {
// OK, we're started, everything went fine, just return
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitForStart", "started");
}
return;
} else if (startException instanceof CommunicationException) {
// There was some exception during the starting phase.
// Cast and throw...
//
throw (CommunicationException)startException;
} else if (startException instanceof InterruptedException) {
// There was some exception during the starting phase.
// Cast and throw...
//
throw (InterruptedException)startException;
} else if (startException != null) {
// There was some exception during the starting phase.
// Wrap and throw...
//
throw new CommunicationException(startException,
"Failed to start: "+
startException);
} else {
// We're not ONLINE, and there's no exception...
// Something went wrong but we don't know what...
//
throw new CommunicationException("Failed to start: state is "+
getStringForState(state));
}
}
}
/**
* Gets the state of this CommunicatorServer
as an integer.
*
* @return ONLINE
, OFFLINE
,
* STARTING
or STOPPING
.
*/
public int getState() {
synchronized (stateLock) {
return state ;
}
}
/**
* Gets the state of this CommunicatorServer
as a string.
*
* @return One of the strings "ONLINE", "OFFLINE", "STARTING" or
* "STOPPING".
*/
public String getStateString() {
return getStringForState(state) ;
}
/**
* Gets the host name used by this CommunicatorServer
.
*
* @return The host name used by this CommunicatorServer
.
*/
public String getHost() {
try {
host = InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
host = "Unknown host";
}
return host ;
}
/**
* Gets the port number used by this CommunicatorServer
.
*
* @return The port number used by this CommunicatorServer
.
*/
public int getPort() {
synchronized (stateLock) {
return port ;
}
}
/**
* Sets the port number used by this CommunicatorServer
.
*
* @param port The port number used by this
* CommunicatorServer
.
*
* @exception java.lang.IllegalStateException This method has been invoked
* while the communicator was ONLINE or STARTING.
*/
public void setPort(int port) throws java.lang.IllegalStateException {
synchronized (stateLock) {
if ((state == ONLINE) || (state == STARTING))
throw new IllegalStateException("Stop server before " +
"carrying out this operation");
this.port = port;
dbgTag = makeDebugTag();
}
}
/**
* Gets the protocol being used by this CommunicatorServer
.
* @return The protocol as a string.
*/
public abstract String getProtocol() ;
/**
* Gets the number of clients that have been processed by this
* CommunicatorServer
since its creation.
*
* @return The number of clients handled by this
* CommunicatorServer
* since its creation. This counter is not reset by the
* stop
method.
*/
int getServedClientCount() {
return servedClientCount ;
}
/**
* Gets the number of clients currently being processed by this
* CommunicatorServer
.
*
* @return The number of clients currently being processed by this
* CommunicatorServer
.
*/
int getActiveClientCount() {
int result = clientHandlerVector.size() ;
return result ;
}
/**
* Gets the maximum number of clients that this
* CommunicatorServer
can process concurrently.
*
* @return The maximum number of clients that this
* CommunicatorServer
can
* process concurrently.
*/
int getMaxActiveClientCount() {
return maxActiveClientCount ;
}
/**
* Sets the maximum number of clients this
* CommunicatorServer
can process concurrently.
*
* @param c The number of clients.
*
* @exception java.lang.IllegalStateException This method has been invoked
* while the communicator was ONLINE or STARTING.
*/
void setMaxActiveClientCount(int c)
throws java.lang.IllegalStateException {
synchronized (stateLock) {
if ((state == ONLINE) || (state == STARTING)) {
throw new IllegalStateException(
"Stop server before carrying out this operation");
}
maxActiveClientCount = c ;
}
}
/**
* For SNMP Runtime internal use only.
*/
void notifyClientHandlerCreated(ClientHandler h) {
clientHandlerVector.addElement(h) ;
}
/**
* For SNMP Runtime internal use only.
*/
synchronized void notifyClientHandlerDeleted(ClientHandler h) {
clientHandlerVector.removeElement(h);
notifyAll();
}
/**
* The number of times the communicator server will attempt
* to bind before giving up.
**/
protected int getBindTries() {
return 50;
}
/**
* The delay, in ms, during which the communicator server will sleep before
* attempting to bind again.
**/
protected long getBindSleepTime() {
return 100;
}
/**
* For SNMP Runtime internal use only.
*
* The run
method executed by this connector's main thread.
*/
public void run() {
// Fix jaw.00667.B
// It seems that the init of "i" and "success"
// need to be done outside the "try" clause...
// A bug in Java 2 production release ?
//
int i = 0;
boolean success = false;
// ----------------------
// Bind
// ----------------------
try {
// Fix for bug 4352451: "java.net.BindException: Address in use".
//
final int bindRetries = getBindTries();
final long sleepTime = getBindSleepTime();
while (i < bindRetries && !success) {
try {
// Try socket connection.
//
doBind();
success = true;
} catch (CommunicationException ce) {
i++;
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ie) {
throw ie;
}
}
}
// Retry last time to get correct exception.
//
if (!success) {
// Try socket connection.
//
doBind();
}
} catch(Exception x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"run", "Got unexpected exception", x);
}
synchronized(stateLock) {
startException = x;
changeState(OFFLINE);
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"run","State is OFFLINE");
}
doError(x);
return;
}
try {
// ----------------------
// State change
// ----------------------
changeState(ONLINE) ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"run","State is ONLINE");
}
// ----------------------
// Main loop
// ----------------------
while (!stopRequested) {
servedClientCount++;
doReceive() ;
waitIfTooManyClients() ;
doProcess() ;
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"run","Stop has been requested");
}
} catch(InterruptedException x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"run","Interrupt caught");
}
changeState(STOPPING);
} catch(Exception x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"run","Got unexpected exception", x);
}
changeState(STOPPING);
} finally {
synchronized (stateLock) {
interrupted = true;
Thread.currentThread().interrupted();
}
// ----------------------
// unBind
// ----------------------
try {
doUnbind() ;
waitClientTermination() ;
changeState(OFFLINE);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"run","State is OFFLINE");
}
} catch(Exception x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"run","Got unexpected exception", x);
}
changeState(OFFLINE);
}
}
}
/**
*/
protected abstract void doError(Exception e) throws CommunicationException;
//
// To be defined by the subclass.
//
// Each method below is called by run() and must be subclassed.
// If the method sends an exception (Communication or Interrupt), this
// will end up the run() method and switch the connector offline.
//
// If it is a CommunicationException, run() will call
// Debug.printException().
//
// All these methods should propagate the InterruptedException to inform
// run() that the connector must be switch OFFLINE.
//
//
//
// doBind() should do all what is needed before calling doReceive().
// If doBind() throws an exception, doUnbind() is not to be called
// and run() ends up.
//
/**
*/
protected abstract void doBind()
throws CommunicationException, InterruptedException ;
/**
* doReceive()
should block until a client is available.
* If this method throws an exception, doProcess()
is not
* called but doUnbind()
is called then run()
* stops.
*/
protected abstract void doReceive()
throws CommunicationException, InterruptedException ;
/**
* doProcess()
is called after doReceive()
:
* it should process the requests of the incoming client.
* If it throws an exception, doUnbind()
is called and
* run()
stops.
*/
protected abstract void doProcess()
throws CommunicationException, InterruptedException ;
/**
* doUnbind()
is called whenever the connector goes
* OFFLINE
, except if doBind()
has thrown an
* exception.
*/
protected abstract void doUnbind()
throws CommunicationException, InterruptedException ;
/**
* Get the MBeanServer
object to which incoming requests are
* sent. This is either the MBean server in which this connector is
* registered, or an MBeanServerForwarder
leading to that
* server.
*/
public synchronized MBeanServer getMBeanServer() {
return topMBS;
}
/**
* Set the MBeanServer
object to which incoming
* requests are sent. This must be either the MBean server in
* which this connector is registered, or an
* MBeanServerForwarder
leading to that server. An
* MBeanServerForwarder
mbsf
leads to an
* MBean server mbs
if
* mbsf.getMBeanServer()
is either mbs
* or an MBeanServerForwarder
leading to
* mbs
.
*
* @exception IllegalArgumentException if newMBS
is neither
* the MBean server in which this connector is registered nor an
* MBeanServerForwarder
leading to that server.
*
* @exception IllegalStateException This method has been invoked
* while the communicator was ONLINE or STARTING.
*/
public synchronized void setMBeanServer(MBeanServer newMBS)
throws IllegalArgumentException, IllegalStateException {
synchronized (stateLock) {
if (state == ONLINE || state == STARTING)
throw new IllegalStateException("Stop server before " +
"carrying out this operation");
}
final String error =
"MBeanServer argument must be MBean server where this " +
"server is registered, or an MBeanServerForwarder " +
"leading to that server";
Vectorwait()
is terminated when a client handler
* thread calls notifyClientHandlerDeleted(this)
;
*/
private synchronized void waitIfTooManyClients()
throws InterruptedException {
while (getActiveClientCount() >= maxActiveClientCount) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitIfTooManyClients","Waiting for a client to terminate");
}
wait();
}
}
/**
* This method blocks until there is no more active client.
*/
private void waitClientTermination() {
int s = clientHandlerVector.size() ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
if (s >= 1) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitClientTermination","waiting for " +
s + " clients to terminate");
}
}
// The ClientHandler will remove themselves from the
// clientHandlerVector at the end of their run() method, by
// calling notifyClientHandlerDeleted().
// Since the clientHandlerVector is modified by the ClientHandler
// threads we must avoid using Enumeration or Iterator to loop
// over this array. We must also take care of NoSuchElementException
// which could be thrown if the last ClientHandler removes itself
// between the call to clientHandlerVector.isEmpty() and the call
// to clientHandlerVector.firstElement().
// What we *MUST NOT DO* is locking the clientHandlerVector, because
// this would most probably cause a deadlock.
//
while (! clientHandlerVector.isEmpty()) {
try {
clientHandlerVector.firstElement().join();
} catch (NoSuchElementException x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitClientTermination","No elements left", x);
}
}
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
if (s >= 1) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitClientTermination","Ok, let's go...");
}
}
}
/**
* Call interrupt()
on each pending client.
*/
private void terminateAllClient() {
final int s = clientHandlerVector.size() ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
if (s >= 1) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"terminateAllClient","Interrupting " + s + " clients");
}
}
// The ClientHandler will remove themselves from the
// clientHandlerVector at the end of their run() method, by
// calling notifyClientHandlerDeleted().
// Since the clientHandlerVector is modified by the ClientHandler
// threads we must avoid using Enumeration or Iterator to loop
// over this array.
// We cannot use the same logic here than in waitClientTermination()
// because there is no guarantee that calling interrupt() on the
// ClientHandler will actually terminate the ClientHandler.
// Since we do not want to wait for the actual ClientHandler
// termination, we cannot simply loop over the array until it is
// empty (this might result in calling interrupt() endlessly on
// the same client handler. So what we do is simply take a snapshot
// copy of the vector and loop over the copy.
// What we *MUST NOT DO* is locking the clientHandlerVector, because
// this would most probably cause a deadlock.
//
final ClientHandler[] handlers =
clientHandlerVector.toArray(new ClientHandler[0]);
for (ClientHandler h : handlers) {
try {
h.interrupt() ;
} catch (Exception x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"terminateAllClient",
"Failed to interrupt pending request. " +
"Ignore the exception.", x);
}
}
}
}
/**
* Controls the way the CommunicatorServer service is deserialized.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
// Call the default deserialization of the object.
//
stream.defaultReadObject();
// Call the specific initialization for the CommunicatorServer service.
// This is for transient structures to be initialized to specific
// default values.
//
stateLock = new Object();
state = OFFLINE;
stopRequested = false;
servedClientCount = 0;
clientHandlerVector = new VectorMBeanServer
in which the MBean will
* be registered.
*@param name The object name of the MBean.
*
*@return The name of the MBean registered.
*
*@exception java.langException This exception should be caught by
* the MBeanServer
and re-thrown
* as an MBeanRegistrationException
.
*/
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws java.lang.Exception {
objectName = name;
synchronized (this) {
if (bottomMBS != null) {
throw new IllegalArgumentException("connector already " +
"registered in an MBean " +
"server");
}
topMBS = bottomMBS = server;
}
dbgTag = makeDebugTag();
return name;
}
/**
*
*@param registrationDone Indicates whether or not the MBean has been
* successfully registered in the MBeanServer
.
* The value false means that the registration phase has failed.
*/
public void postRegister(Boolean registrationDone) {
if (!registrationDone.booleanValue()) {
synchronized (this) {
topMBS = bottomMBS = null;
}
}
}
/**
* Stop the connector.
*
* @exception java.langException This exception should be caught by
* the MBeanServer
and re-thrown
* as an MBeanRegistrationException
.
*/
public void preDeregister() throws java.lang.Exception {
synchronized (this) {
topMBS = bottomMBS = null;
}
objectName = null ;
final int cstate = getState();
if ((cstate == ONLINE) || ( cstate == STARTING)) {
stop() ;
}
}
/**
* Do nothing.
*/
public void postDeregister(){
}
/**
* Load a class using the default loader repository
**/
Class loadClass(String className)
throws ClassNotFoundException {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
final ClassLoaderRepository clr =
MBeanServerFactory.getClassLoaderRepository(bottomMBS);
if (clr == null) throw new ClassNotFoundException(className);
return clr.loadClass(className);
}
}
}