/*
* @(#)file CommunicatorServer.java
* @(#)author Sun Microsystems, Inc.
* @(#)version 1.58
* @(#)lastedit 04/02/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
package com.sun.jmx.snmp.daemon;
// java import
//
import java.io.ObjectInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Date;
import java.util.Vector;
import java.util.Enumeration;
// 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;
// jmx RI import
//
import com.sun.jmx.trace.Trace;
// 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.
* @version 1.58 02/19/04 * @author Sun Microsystems, Inc */ 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 Vector clientHandlerVector = new Vector() ;
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
.
*
* @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 :
infoType = Trace.INFO_ADAPTOR_SNMP ;
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 (isTraceOn())
trace("start","Connector is not OFFLINE") ;
return;
}
if (isTraceOn())
trace("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) {
// can not happen because of `0'
trace("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 (isTraceOn())
trace("stop","Connector is not ONLINE") ;
return;
}
changeState(STOPPING);
//
// Stop the connector thread
//
if (isTraceOn())
trace("stop","Interrupt main thread") ;
stopRequested = true ;
if (!interrupted) {
interrupted = true;
mainThread.interrupt();
}
}
//
// Call terminate on each active client handler
//
if (isTraceOn()) {
trace("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 (isTraceOn())
trace("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 (isTraceOn())
trace("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 (isTraceOn())
trace("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 (isTraceOn()) trace("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 (isDebugOn()) {
debug("run","Unexpected exception = "+x) ;
}
synchronized(stateLock) {
startException = x;
changeState(OFFLINE);
}
if (isTraceOn()) {
trace("run","State is OFFLINE") ;
}
doError(x);
return;
}
try {
// ----------------------
// State change
// ----------------------
changeState(ONLINE) ;
if (isTraceOn()) {
trace("run","State is ONLINE") ;
}
// ----------------------
// Main loop
// ----------------------
while (!stopRequested) {
servedClientCount++;
doReceive() ;
waitIfTooManyClients() ;
doProcess() ;
}
if (isTraceOn()) {
trace("run","Stop has been requested") ;
}
} catch(InterruptedException x) {
if (isTraceOn()) {
trace("run","Interrupt caught") ;
}
changeState(STOPPING);
} catch(Exception x) {
if (isDebugOn()) {
debug("run","Unexpected exception = "+x) ;
}
changeState(STOPPING);
} finally {
synchronized (stateLock) {
interrupted = true;
Thread.currentThread().interrupted();
}
// ----------------------
// unBind
// ----------------------
try {
doUnbind() ;
waitClientTermination() ;
changeState(OFFLINE);
if (isTraceOn()) {
trace("run","State is OFFLINE") ;
}
} catch(Exception x) {
if (isDebugOn()) {
debug("run","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";
Vector seenMBS = new Vector();
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.
*/
ObjectName getObjectName() {
return objectName ;
}
/**
* 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.
*/
String makeDebugTag() {
return "CommunicatorServer["+ getProtocol() + ":" + getPort() + "]" ;
}
/**
* 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)
;
*/
private synchronized void waitIfTooManyClients()
throws InterruptedException {
while (getActiveClientCount() >= maxActiveClientCount) {
if (isTraceOn()) {
trace("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 (isTraceOn()) {
if (s >= 1) {
trace("waitClientTermination","waiting for " +
s + " clients to terminate") ;
}
}
for (Enumeration e = clientHandlerVector.elements() ;
e.hasMoreElements();){
ClientHandler h = (ClientHandler)e.nextElement() ;
h.join() ;
}
if (isTraceOn()) {
if (s >= 1) {
trace("waitClientTermination","Ok, let's go...") ;
}
}
}
/**
* Call interrupt()
on each pending client.
*/
private void terminateAllClient() {
int s = clientHandlerVector.size() ;
if (isTraceOn()) {
if (s >= 1) {
trace("terminateAllClient","Interrupting " + s + " clients") ;
}
}
for (Enumeration e = clientHandlerVector.elements() ;
e.hasMoreElements();){
ClientHandler h = (ClientHandler)e.nextElement() ;
h.interrupt() ;
}
}
/**
* 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();
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 {@link javax.management.AttributeChangeNotification},
* sent when the State 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 (isDebugOn()) {
debug("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.
*
* @param listener The listener object to be removed.
*
* @exception ListenerNotFoundException The listener is not registered.
*/
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
if (isDebugOn()) {
debug("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 {@link javax.management.AttributeChangeNotification},
* sent when the State 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 (isDebugOn()) {
debug("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.
*
*@param server The MBeanServer
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);
}
}
//
// Debug stuff
//
/**
*/
int infoType;
/**
*/
boolean isTraceOn() {
return Trace.isSelected(Trace.LEVEL_TRACE, infoType);
}
/**
*/
void trace(String clz, String func, String info) {
Trace.send(Trace.LEVEL_TRACE, infoType, clz, func, info);
}
/**
*/
boolean isDebugOn() {
return Trace.isSelected(Trace.LEVEL_DEBUG, infoType);
}
/**
*/
void debug(String clz, String func, String info) {
Trace.send(Trace.LEVEL_DEBUG, infoType, clz, func, info);
}
/**
*/
void debug(String clz, String func, Throwable exception) {
Trace.send(Trace.LEVEL_DEBUG, infoType, clz, func, exception);
}
/**
*/
void trace(String func, String info) {
trace(dbgTag, func, info);
}
/**
*/
void debug(String func, String info) {
debug(dbgTag, func, info);
}
/**
*/
void debug(String func, Throwable exception) {
debug(dbgTag, func, exception);
}
}