/*
* 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
AttributeChangeNotification
to the
registered listeners, if any.
This API is a Sun Microsystems internal API and is subject
to change without notice.
/**
* Defines generic behavior for the server part of a connector or an adaptor.
* Most connectors or adaptors extend <CODE>CommunicatorServer</CODE>
* and inherit this behavior. Connectors or adaptors that do not fit into
* this model do not extend <CODE>CommunicatorServer</CODE>.
* <p>
* A <CODE>CommunicatorServer</CODE> is an active object, it listens for
* client requests and processes them in its own thread. When necessary, a
* <CODE>CommunicatorServer</CODE> creates other threads to process multiple
* requests concurrently.
* <p>
* A <CODE>CommunicatorServer</CODE> object can be stopped by calling the
* <CODE>stop</CODE> method. When it is stopped, the
* <CODE>CommunicatorServer</CODE> no longer listens to client requests and
* no longer holds any thread or communication resources.
* It can be started again by calling the <CODE>start</CODE> method.
* <p>
* A <CODE>CommunicatorServer</CODE> has a <CODE>State</CODE> attribute
* which reflects its activity.
* <p>
* <TABLE>
* <TR><TH>CommunicatorServer</TH> <TH>State</TH></TR>
* <TR><TD><CODE>stopped</CODE></TD> <TD><CODE>OFFLINE</CODE></TD></TR>
* <TR><TD><CODE>starting</CODE></TD> <TD><CODE>STARTING</CODE></TD></TR>
* <TR><TD><CODE>running</CODE></TD> <TD><CODE>ONLINE</CODE></TD></TR>
* <TR><TD><CODE>stopping</CODE></TD> <TD><CODE>STOPPING</CODE></TD></TR>
* </TABLE>
* <p>
* The <CODE>STARTING</CODE> state marks the transition
* from <CODE>OFFLINE</CODE> to <CODE>ONLINE</CODE>.
* <p>
* The <CODE>STOPPING</CODE> state marks the transition from
* <CODE>ONLINE</CODE> to <CODE>OFFLINE</CODE>. This occurs when the
* <CODE>CommunicatorServer</CODE> is finishing or interrupting active
* requests.
* <p>
* When a <CODE>CommunicatorServer</CODE> is unregistered from the MBeanServer,
* it is stopped automatically.
* <p>
* When the value of the <CODE>State</CODE> attribute changes the
* <CODE>CommunicatorServer</CODE> sends a
* <tt>{@link javax.management.AttributeChangeNotification}</tt> to the
* registered listeners, if any.
*
* <p><b>This API is a Sun Microsystems internal API and is subject
* to change without notice.</b></p>
*/
public abstract class CommunicatorServer
implements Runnable, MBeanRegistration, NotificationBroadcaster,
CommunicatorServerMBean {
//
// States of a CommunicatorServer
//
Represents an ONLINE
state.
/**
* Represents an <CODE>ONLINE</CODE> state.
*/
public static final int ONLINE = 0 ;
Represents an OFFLINE
state.
/**
* Represents an <CODE>OFFLINE</CODE> state.
*/
public static final int OFFLINE = 1 ;
Represents a STOPPING
state.
/**
* Represents a <CODE>STOPPING</CODE> state.
*/
public static final int STOPPING = 2 ;
Represents a STARTING
state.
/**
* Represents a <CODE>STARTING</CODE> 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.
/**
* 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.
/**
* The state of the connector server.
*/
transient volatile int state = OFFLINE ;
The object name of the connector server.
@serial
/**
* 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
/**
* 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
/**
* The host name used by this CommunicatorServer.
* @serial
*/
String host = null ;
The port number used by this CommunicatorServer.
@serial
/**
* 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 Vector<ClientHandler>
clientHandlerVector = new Vector<ClientHandler>() ;
private transient Thread fatherThread = Thread.currentThread() ;
private transient Thread mainThread = null ;
private volatile boolean stopRequested = false ;
private boolean interrupted = false;
private transient Exception startException = null;
// Notifs count, broadcaster and info
private transient long notifCount = 0;
private transient NotificationBroadcasterSupport notifBroadcaster =
new NotificationBroadcasterSupport();
private transient MBeanNotificationInfo[] notifInfos = null;
Instantiates a CommunicatorServer
.
Params: - connectorType – Indicates the connector type. Possible values are:
SNMP_TYPE.
Throws: -
java.lang.IllegalArgumentException
– This connector type is not correct.
/**
* Instantiates a <CODE>CommunicatorServer</CODE>.
*
* @param connectorType Indicates the connector type. Possible values are:
* SNMP_TYPE.
*
* @exception <CODE>java.lang.IllegalArgumentException</CODE>
* 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
.
Params: - 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.
Throws: - CommunicationException – if the connectors fails to start.
- InterruptedException – if the thread is interrupted or the
timeout expires.
/**
* Starts this <CODE>CommunicatorServer</CODE>.
* <p>
* Has no effect if this <CODE>CommunicatorServer</CODE> is
* <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
* @param timeout Time in ms to wait for the connector to start.
* If <code>timeout</code> is positive, wait for at most
* the specified time. An infinite timeout can be specified
* by passing a <code>timeout</code> value equals
* <code>Long.MAX_VALUE</code>. 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
.
/**
* Starts this <CODE>CommunicatorServer</CODE>.
* <p>
* Has no effect if this <CODE>CommunicatorServer</CODE> is
* <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
*/
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
.
/**
* Stops this <CODE>CommunicatorServer</CODE>.
* <p>
* Has no effect if this <CODE>CommunicatorServer</CODE> is
* <CODE>OFFLINE</CODE> or <CODE>STOPPING</CODE>.
*/
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.
Returns: True if connector is ONLINE
; false otherwise.
/**
* Tests whether the <CODE>CommunicatorServer</CODE> is active.
*
* @return True if connector is <CODE>ONLINE</CODE>; 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:
- if timeOut is negative then
waitState
returns immediately (i.e. does not wait at all),
- if timeOut equals zero then
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).
Params: - wantedState – The value of this MBean's State attribute to wait
for. wantedState can be one of:
CommunicatorServer.OFFLINE
,
CommunicatorServer.ONLINE
,
CommunicatorServer.STARTING
,
CommunicatorServer.STOPPING
.
- timeOut – The maximum time to wait for, in milliseconds,
if positive.
Infinite time out if 0, or no waiting at all if negative.
Returns: true if the value of this MBean's State attribute is the
same as the wantedState parameter; false otherwise.
/**
* <p>Waits until either the State attribute of this MBean equals the
* specified <VAR>wantedState</VAR> parameter,
* or the specified <VAR>timeOut</VAR> has elapsed.
* The method <CODE>waitState</CODE> returns with a boolean value
* indicating whether the specified <VAR>wantedState</VAR> parameter
* equals the value of this MBean's State attribute at the time the method
* terminates.</p>
*
* <p>Two special cases for the <VAR>timeOut</VAR> parameter value are:</p>
* <UL><LI> if <VAR>timeOut</VAR> is negative then <CODE>waitState</CODE>
* returns immediately (i.e. does not wait at all),</LI>
* <LI> if <VAR>timeOut</VAR> equals zero then <CODE>waitState</CODE>
* waits untill the value of this MBean's State attribute
* is the same as the <VAR>wantedState</VAR> parameter (i.e. will wait
* indefinitely if this condition is never met).</LI></UL>
*
* @param wantedState The value of this MBean's State attribute to wait
* for. <VAR>wantedState</VAR> can be one of:
* <ul>
* <li><CODE>CommunicatorServer.OFFLINE</CODE>,</li>
* <li><CODE>CommunicatorServer.ONLINE</CODE>,</li>
* <li><CODE>CommunicatorServer.STARTING</CODE>,</li>
* <li><CODE>CommunicatorServer.STOPPING</CODE>.</li>
* </ul>
* @param timeOut The maximum time to wait for, in milliseconds,
* if positive.
* Infinite time out if 0, or no waiting at all if negative.
*
* @return true if the value of this MBean's State attribute is the
* same as the <VAR>wantedState</VAR> parameter; false otherwise.
*/
public boolean waitState(int wantedState, long timeOut) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitState", wantedState + "(0on,1off,2st) TO=" + timeOut +
" ; current state = " + getStateString());
}
long endTime = 0;
if (timeOut > 0)
endTime = System.currentTimeMillis() + timeOut;
synchronized (stateLock) {
while (state != wantedState) {
if (timeOut < 0) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitState", "timeOut < 0, return without wait");
}
return false;
} else {
try {
if (timeOut > 0) {
long toWait = endTime - System.currentTimeMillis();
if (toWait <= 0) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitState", "timed out");
}
return false;
}
stateLock.wait(toWait);
} else { // timeOut == 0
stateLock.wait();
}
} catch (InterruptedException e) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitState", "wait interrupted");
}
return (state == wantedState);
}
}
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"waitState","returning in desired state");
}
return true;
}
}
Waits until the communicator is started or timeout expires.
Params: - 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.
Throws: - CommunicationException – if the connectors fails to start.
- InterruptedException – if the thread is interrupted or the
timeout expires.
/**
* <p>Waits until the communicator is started or timeout expires.
*
* @param timeout Time in ms to wait for the connector to start.
* If <code>timeout</code> is positive, wait for at most
* the specified time. An infinite timeout can be specified
* by passing a <code>timeout</code> value equals
* <code>Long.MAX_VALUE</code>. 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.
Returns: ONLINE
, OFFLINE
,
STARTING
or STOPPING
.
/**
* Gets the state of this <CODE>CommunicatorServer</CODE> as an integer.
*
* @return <CODE>ONLINE</CODE>, <CODE>OFFLINE</CODE>,
* <CODE>STARTING</CODE> or <CODE>STOPPING</CODE>.
*/
public int getState() {
synchronized (stateLock) {
return state ;
}
}
Gets the state of this CommunicatorServer
as a string.
Returns: One of the strings "ONLINE", "OFFLINE", "STARTING" or
"STOPPING".
/**
* Gets the state of this <CODE>CommunicatorServer</CODE> 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
.
Returns: The host name used by this CommunicatorServer
.
/**
* Gets the host name used by this <CODE>CommunicatorServer</CODE>.
*
* @return The host name used by this <CODE>CommunicatorServer</CODE>.
*/
public String getHost() {
try {
host = InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
host = "Unknown host";
}
return host ;
}
Gets the port number used by this CommunicatorServer
.
Returns: The port number used by this CommunicatorServer
.
/**
* Gets the port number used by this <CODE>CommunicatorServer</CODE>.
*
* @return The port number used by this <CODE>CommunicatorServer</CODE>.
*/
public int getPort() {
synchronized (stateLock) {
return port ;
}
}
Sets the port number used by this CommunicatorServer
.
Params: - port – The port number used by this
CommunicatorServer
.
Throws: - IllegalStateException – This method has been invoked
while the communicator was ONLINE or STARTING.
/**
* Sets the port number used by this <CODE>CommunicatorServer</CODE>.
*
* @param port The port number used by this
* <CODE>CommunicatorServer</CODE>.
*
* @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
.
Returns: The protocol as a string.
/**
* Gets the protocol being used by this <CODE>CommunicatorServer</CODE>.
* @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.
Returns: The number of clients handled by this
CommunicatorServer
since its creation. This counter is not reset by the
stop
method.
/**
* Gets the number of clients that have been processed by this
* <CODE>CommunicatorServer</CODE> since its creation.
*
* @return The number of clients handled by this
* <CODE>CommunicatorServer</CODE>
* since its creation. This counter is not reset by the
* <CODE>stop</CODE> method.
*/
int getServedClientCount() {
return servedClientCount ;
}
Gets the number of clients currently being processed by this
CommunicatorServer
.
Returns: The number of clients currently being processed by this
CommunicatorServer
.
/**
* Gets the number of clients currently being processed by this
* <CODE>CommunicatorServer</CODE>.
*
* @return The number of clients currently being processed by this
* <CODE>CommunicatorServer</CODE>.
*/
int getActiveClientCount() {
int result = clientHandlerVector.size() ;
return result ;
}
Gets the maximum number of clients that this
CommunicatorServer
can process concurrently.
Returns: The maximum number of clients that this
CommunicatorServer
can
process concurrently.
/**
* Gets the maximum number of clients that this
* <CODE>CommunicatorServer</CODE> can process concurrently.
*
* @return The maximum number of clients that this
* <CODE>CommunicatorServer</CODE> can
* process concurrently.
*/
int getMaxActiveClientCount() {
return maxActiveClientCount ;
}
Sets the maximum number of clients this
CommunicatorServer
can process concurrently.
Params: - c – The number of clients.
Throws: - IllegalStateException – This method has been invoked
while the communicator was ONLINE or STARTING.
/**
* Sets the maximum number of clients this
* <CODE>CommunicatorServer</CODE> 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.
/**
* For SNMP Runtime internal use only.
*/
void notifyClientHandlerCreated(ClientHandler h) {
clientHandlerVector.addElement(h) ;
}
For SNMP Runtime internal use only.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* For SNMP Runtime internal use only.
* <p>
* The <CODE>run</CODE> 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.
/**
* <CODE>doReceive()</CODE> should block until a client is available.
* If this method throws an exception, <CODE>doProcess()</CODE> is not
* called but <CODE>doUnbind()</CODE> is called then <CODE>run()</CODE>
* 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.
/**
* <CODE>doProcess()</CODE> is called after <CODE>doReceive()</CODE>:
* it should process the requests of the incoming client.
* If it throws an exception, <CODE>doUnbind()</CODE> is called and
* <CODE>run()</CODE> stops.
*/
protected abstract void doProcess()
throws CommunicationException, InterruptedException ;
doUnbind()
is called whenever the connector goes
OFFLINE
, except if doBind()
has thrown an
exception.
/**
* <CODE>doUnbind()</CODE> is called whenever the connector goes
* <CODE>OFFLINE</CODE>, except if <CODE>doBind()</CODE> 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.
/**
* Get the <code>MBeanServer</code> object to which incoming requests are
* sent. This is either the MBean server in which this connector is
* registered, or an <code>MBeanServerForwarder</code> 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
.
Throws: - IllegalArgumentException – if
newMBS
is neither
the MBean server in which this connector is registered nor an
MBeanServerForwarder
leading to that server. - IllegalStateException – This method has been invoked
while the communicator was ONLINE or STARTING.
/**
* Set the <code>MBeanServer</code> object to which incoming
* requests are sent. This must be either the MBean server in
* which this connector is registered, or an
* <code>MBeanServerForwarder</code> leading to that server. An
* <code>MBeanServerForwarder</code> <code>mbsf</code> leads to an
* MBean server <code>mbs</code> if
* <code>mbsf.getMBeanServer()</code> is either <code>mbs</code>
* or an <code>MBeanServerForwarder</code> leading to
* <code>mbs</code>.
*
* @exception IllegalArgumentException if <code>newMBS</code> is neither
* the MBean server in which this connector is registered nor an
* <code>MBeanServerForwarder</code> 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";
Vector<MBeanServer> seenMBS = new Vector<MBeanServer>();
for (MBeanServer mbs = newMBS;
mbs != bottomMBS;
mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) {
if (!(mbs instanceof MBeanServerForwarder))
throw new IllegalArgumentException(error);
if (seenMBS.contains(mbs))
throw new IllegalArgumentException("MBeanServerForwarder " +
"loop");
seenMBS.addElement(mbs);
}
topMBS = newMBS;
}
//
// To be called by the subclass if needed
//
For internal use only.
/**
* For internal use only.
*/
ObjectName getObjectName() {
return objectName ;
}
For internal use only.
/**
* For internal use only.
*/
void changeState(int newState) {
int oldState;
synchronized (stateLock) {
if (state == newState)
return;
oldState = state;
state = newState;
stateLock.notifyAll();
}
sendStateChangeNotification(oldState, newState);
}
Returns the string used in debug traces.
/**
* Returns the string used in debug traces.
*/
String makeDebugTag() {
return "CommunicatorServer["+ getProtocol() + ":" + getPort() + "]" ;
}
Returns the string used to name the connector thread.
/**
* Returns the string used to name the connector thread.
*/
String makeThreadName() {
String result ;
if (objectName == null)
result = "CommunicatorServer" ;
else
result = objectName.toString() ;
return result ;
}
This method blocks if there are too many active clients.
Call to wait()
is terminated when a client handler
thread calls notifyClientHandlerDeleted(this)
;
/**
* This method blocks if there are too many active clients.
* Call to <CODE>wait()</CODE> is terminated when a client handler
* thread calls <CODE>notifyClientHandlerDeleted(this)</CODE> ;
*/
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.
/**
* 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.
/**
* Call <CODE>interrupt()</CODE> 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.
/**
* 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 Vector<ClientHandler>();
fatherThread = Thread.currentThread();
mainThread = null;
notifCount = 0;
notifInfos = null;
notifBroadcaster = new NotificationBroadcasterSupport();
dbgTag = makeDebugTag();
}
//
// NotificationBroadcaster
//
Adds a listener for the notifications emitted by this
CommunicatorServer.
There is only one type of notifications sent by the CommunicatorServer:
they are AttributeChangeNotification
,
sent when the State attribute of this CommunicatorServer
changes.
Params: - listener – The listener object which will handle the emitted
notifications.
- filter – The filter object. If filter is null, no filtering
will be performed before handling notifications.
- handback – An object which will be sent back unchanged to the
listener when a notification is emitted.
Throws: - IllegalArgumentException – Listener parameter is null.
/**
* Adds a listener for the notifications emitted by this
* CommunicatorServer.
* There is only one type of notifications sent by the CommunicatorServer:
* they are <tt>{@link javax.management.AttributeChangeNotification}</tt>,
* sent when the <tt>State</tt> attribute of this CommunicatorServer
* changes.
*
* @param listener The listener object which will handle the emitted
* notifications.
* @param filter The filter object. If filter is null, no filtering
* will be performed before handling notifications.
* @param handback An object which will be sent back unchanged to the
* listener when a notification is emitted.
*
* @exception IllegalArgumentException Listener parameter is null.
*/
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
throws java.lang.IllegalArgumentException {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"addNotificationListener","Adding listener "+ listener +
" with filter "+ filter + " and handback "+ handback);
}
notifBroadcaster.addNotificationListener(listener, filter, handback);
}
Removes the specified listener from this CommunicatorServer.
Note that if the listener has been registered with different
handback objects or notification filters, all entries corresponding
to the listener will be removed.
Params: - listener – The listener object to be removed.
Throws: - ListenerNotFoundException – The listener is not registered.
/**
* Removes the specified listener from this CommunicatorServer.
* Note that if the listener has been registered with different
* handback objects or notification filters, all entries corresponding
* to the listener will be removed.
*
* @param listener The listener object to be removed.
*
* @exception ListenerNotFoundException The listener is not registered.
*/
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"removeNotificationListener","Removing listener "+ listener);
}
notifBroadcaster.removeNotificationListener(listener);
}
Returns an array of MBeanNotificationInfo objects describing
the notification types sent by this CommunicatorServer.
There is only one type of notifications sent by the CommunicatorServer:
it is AttributeChangeNotification
,
sent when the State attribute of this CommunicatorServer
changes.
/**
* Returns an array of MBeanNotificationInfo objects describing
* the notification types sent by this CommunicatorServer.
* There is only one type of notifications sent by the CommunicatorServer:
* it is <tt>{@link javax.management.AttributeChangeNotification}</tt>,
* sent when the <tt>State</tt> attribute of this CommunicatorServer
* changes.
*/
public MBeanNotificationInfo[] getNotificationInfo() {
// Initialize notifInfos on first call to getNotificationInfo()
//
if (notifInfos == null) {
notifInfos = new MBeanNotificationInfo[1];
String[] notifTypes = {
AttributeChangeNotification.ATTRIBUTE_CHANGE};
notifInfos[0] = new MBeanNotificationInfo( notifTypes,
AttributeChangeNotification.class.getName(),
"Sent to notify that the value of the State attribute "+
"of this CommunicatorServer instance has changed.");
}
return notifInfos;
}
/**
*
*/
private void sendStateChangeNotification(int oldState, int newState) {
String oldStateString = getStringForState(oldState);
String newStateString = getStringForState(newState);
String message = new StringBuffer().append(dbgTag)
.append(" The value of attribute State has changed from ")
.append(oldState).append(" (").append(oldStateString)
.append(") to ").append(newState).append(" (")
.append(newStateString).append(").").toString();
notifCount++;
AttributeChangeNotification notif =
new AttributeChangeNotification(this, // source
notifCount, // sequence number
System.currentTimeMillis(), // time stamp
message, // message
"State", // attribute name
"int", // attribute type
new Integer(oldState), // old value
new Integer(newState) ); // new value
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"sendStateChangeNotification","Sending AttributeChangeNotification #"
+ notifCount + " with message: "+ message);
}
notifBroadcaster.sendNotification(notif);
}
/**
*
*/
private static String getStringForState(int s) {
switch (s) {
case ONLINE: return "ONLINE";
case STARTING: return "STARTING";
case OFFLINE: return "OFFLINE";
case STOPPING: return "STOPPING";
default: return "UNDEFINED";
}
}
//
// MBeanRegistration
//
Preregister method of connector.
Params: - server – The
MBeanServer
in which the MBean will
be registered. - name – The object name of the MBean.
Throws: - langException – This exception should be caught by
the
MBeanServer
and re-thrown
as an MBeanRegistrationException
.
Returns: The name of the MBean registered.
/**
* Preregister method of connector.
*
*@param server The <CODE>MBeanServer</CODE> 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 <CODE>MBeanServer</CODE> and re-thrown
* as an <CODE>MBeanRegistrationException</CODE>.
*/
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;
}
Params: - registrationDone – Indicates whether or not the MBean has been
successfully registered in the
MBeanServer
.
The value false means that the registration phase has failed.
/**
*
*@param registrationDone Indicates whether or not the MBean has been
* successfully registered in the <CODE>MBeanServer</CODE>.
* 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.
Throws: - langException – This exception should be caught by
the
MBeanServer
and re-thrown
as an MBeanRegistrationException
.
/**
* Stop the connector.
*
* @exception java.langException This exception should be caught by
* the <CODE>MBeanServer</CODE> and re-thrown
* as an <CODE>MBeanRegistrationException</CODE>.
*/
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.
/**
* Do nothing.
*/
public void postDeregister(){
}
Load a class using the default loader repository
/**
* 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);
}
}
}