package javax.slee.management;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.rmi.MarshalledObject;
import javax.management.Notification;
import javax.slee.facilities.AlarmLevel;
import javax.slee.facilities.Level;
/**
* Notifications of this type are emitted by an {@link AlarmMBean} to indicate that
* some component or subsystem in the SLEE is experiencing a problem. Typically, a
* SLEE component uses the {@link javax.slee.facilities.AlarmFacility AlarmFacility}
* to manage alarms.
* <p>
* Alarm notifications contain a {@link NotificationSource} object that can be used
* to obtain more information about the object that raised the alarm. The type of
* an alarm notification can be used to infer the type of the notification source
* object contained in the notification:
* <ul>
* <li>if <code>{@link #getType()} == {@link javax.slee.management.SbbNotification#ALARM_NOTIFICATION_TYPE}</code>
* then the type of the notification source object is {@link javax.slee.management.SbbNotification}.
* <li>if <code>{@link #getType()} == {@link javax.slee.management.ResourceAdaptorEntityNotification#ALARM_NOTIFICATION_TYPE}</code>
* then the type of the notification source object is {@link javax.slee.management.ResourceAdaptorEntityNotification}.
* <li>if <code>{@link #getType()} == {@link javax.slee.management.ProfileTableNotification#ALARM_NOTIFICATION_TYPE}</code>
* then the type of the notification source object is {@link javax.slee.management.ProfileTableNotification}.
* <li>if <code>{@link #getType()} == {@link javax.slee.management.SubsystemNotification#ALARM_NOTIFICATION_TYPE}</code>
* then the type of the notification source object is {@link javax.slee.management.SubsystemNotification}.
* </ul>
* <p>
* As of SLEE 1.1, serialization of this class has been modified to take into account cause
* <code>Throwable</code> objects that may not be deserializable client-side due to classloader
* issues. For example, if the cause of an alarm is an object of a custom exception
* class included in the deployable unit of a component, a generic client may not have
* that class available in its classpath or be able to load it via Java's remote class loading
* mechanisms (eg. the codebase URL could be a <code>file://</code> URL on a different host
* that cannot be resolved on the client host). Serialization of an <code>AlarmNotification</code>
* object containing a cause now includes the stack trace of that cause in the serialization
* stream. If the cause <code>Throwable</code> cannot be later deserialized with the
* <code>AlarmNotification</code> object, a generic <code>java.lang.Exception</code>
* with a message containing the original stack trace is returned as the alarm's cause
* instead.
*/
public final class AlarmNotification extends Notification implements VendorExtensions {
/**
* Create an <code>AlarmNotification</code> to notify listeners of a alarm.
* @param alarmMBean the <code>AlarmMBean</code> object that is emitting
* this notification.
* @param alarmType the type of the alarm being generated. Typically a management client
* should be able to infer the type of the <code>alarmSource</code> object by
* inspecting this type.
* @param alarmSource an object that identifies the object that generated the alarm, for
* example an {@link javax.slee.SbbID}.
* @param alarmLevel the alarm level.
* @param message the alarm message.
* @param cause an optional cause for the alarm.
* @param sequenceNumber the notification sequence number within the source
* <code>AlarmMBean</code> object.
* @param timestamp the time (in ms since January 1, 1970 UTC) that the alarm was generated.
* @throws NullPointerException if <code>notificationSource</code>, <code>alarmType</code>,
* <code>alarmLevel</code>, or <code>message</code> is <code>null</code>.
* @throws IllegalArgumentException if <code>alarmLevel == </code> {@link Level#OFF}.
* @deprecated Alarm notifications have been expanded with new attributes to take advantage
* of the new features provided by the SLEE specification. The
* {@link #AlarmNotification(String,AlarmMBean,String,NotificationSource,String,String,AlarmLevel,String,Throwable,long,long)}
* constructor should be used instead of this constructor.
*/
public AlarmNotification(AlarmMBean alarmMBean, String alarmType, Object alarmSource, Level alarmLevel, String message, Throwable cause, long sequenceNumber, long timestamp) throws NullPointerException, IllegalArgumentException {
super(AlarmMBean.ALARM_NOTIFICATION_TYPE, alarmMBean, sequenceNumber, timestamp, message);
if (alarmMBean == null) throw new NullPointerException("alarmMBean is null");
if (alarmType == null) throw new NullPointerException("alarmType is null");
if (alarmLevel == null) throw new NullPointerException("alarmLevel is null");
if (message == null) throw new NullPointerException("message is null");
if (alarmLevel.isOff()) throw new IllegalArgumentException("alarmLevel cannot be Level.OFF");
this.alarmType = alarmType;
this.alarmSource = alarmSource;
this.level_10 = alarmLevel;
this.cause = cause;
// forward compatibility
this.alarmID = null;
this.notificationSource = null;
this.instanceID = null;
this.level_11 = null;
}
/**
* Create an <code>AlarmNotification</code> to notify listeners of an alarm.
* @param type the JMX type of the notification. The type of the notification
* is typically obtained from the <code>NotificationSource</code> parameter
* when this notification object is created, and can be used by notification
* listeners to infer the type of the <code>NotificationSource</code> and
* hence obtain further information about the source of the notification.
* @param alarmMBean the <code>AlarmMBean</code> object that is emitting this
* notification.
* @param alarmID the unique alarm identifier for the alarm.
* @param notificationSource the component or subsystem in the SLEE that raised
* the alarm.
* @param alarmType an identifier specifying the type of the alarm.
* @param instanceID an identifier specifying the particular instance of the
* alarm type that is occurring.
* @param alarmLevel the alarm level of the notification. This could be {@link AlarmLevel#CLEAR}
* if the alarm has been cleared.
* @param message the alarm message.
* @param cause an optional cause for the alarm notification.
* @param sequenceNumber the notification sequence number within the source
* <code>AlarmMBean</code> object.
* @param timestamp the time (in ms since January 1, 1970 UTC) that the alarm was raised,
* updated, or cleared.
* @throws NullPointerException if <code>type</code>, <code>alarmMBean</code>,
* <code>alarmID</code>, <code>notificationSource</code>, <code>alarmType</code>,
* <code>instanceID</code>, <code>alarmLevel</code>, or <code>message</code>
* is <code>null</code>.
* @since SLEE 1.1
*/
public AlarmNotification(String type, AlarmMBean alarmMBean, String alarmID, NotificationSource notificationSource, String alarmType, String instanceID, AlarmLevel alarmLevel, String message, Throwable cause, long sequenceNumber, long timestamp) throws NullPointerException {
super(type, alarmMBean, sequenceNumber, timestamp, message);
if (type == null) throw new NullPointerException("type is null");
if (alarmMBean == null) throw new NullPointerException("alarmMBean is null");
if (alarmID == null) throw new NullPointerException("alarmID is null");
if (notificationSource == null) throw new NullPointerException("notificationSource is null");
if (alarmType == null) throw new NullPointerException("alarmType is null");
if (instanceID == null) throw new NullPointerException("instanceID is null");
if (alarmLevel == null) throw new NullPointerException("alarmLevel is null");
if (message == null) throw new NullPointerException("message is null");
this.alarmID = alarmID;
this.notificationSource = notificationSource;
this.alarmType = alarmType;
this.instanceID = instanceID;
this.level_11 = alarmLevel;
this.cause = cause;
// backward compatibility
this.alarmSource = notificationSource;
this.level_10 = Level.INFO;
}
/**
* Get the unique alarm identifier of the alarm.
* @return the alarm identifier of the alarm. Returns <code>null</code> for
* SLEE 1.0-compliant notifications.
* @since SLEE 1.1
*/
public String getAlarmID() {
return alarmID;
}
/**
* Get the object that identifies the component or subsystem in the SLEE that
* raised the alarm.
* @return the notification source. Returns <code>null</code> for SLEE 1.0-compliant
* notifications.
* @since SLEE 1.1
*/
public NotificationSource getNotificationSource() {
return notificationSource;
}
/**
* Get the type of the alarm.
* @return the alarm type.
*/
public String getAlarmType() {
return alarmType;
}
/**
* Get the object that identifies the source of the alarm.
* @return the alarm source. Returns the value of {@link #getNotificationSource()}
* for SLEE 1.1-compliant notifications.
* @deprecated Replaced with {@link #getNotificationSource()}.
*/
public Object getAlarmSource() {
return alarmSource;
}
/**
* Get the instance identifier of the alarm type.
* @return the instance identifier. Returns <code>null</code> for SLEE 1.0-compliant
* notifications.
* @since SLEE 1.1
*/
public String getInstanceID() {
return instanceID;
}
/**
* Get the alarm level of the alarm.
* @return the alarm level. Returns {@link Level#INFO} for SLEE 1.1-compliant notifications.
* @deprecated Trace and alarm levels have been split into different classes.
* Replaced with {@link #getAlarmLevel}.
*/
public Level getLevel() {
return level_10;
}
/**
* Get the alarm level of the alarm notification.
* @return the alarm level. Returns <code>null</code> for SLEE 1.0-compliant
* notifications.
* @since SLEE 1.1
*/
public AlarmLevel getAlarmLevel() {
return level_11;
}
/**
* Get the cause for the alarm.
* <p>
* For SLEE 1.1-compliant notifications, this method returns the cause provided
* when the alarm was raised. For SLEE 1.0-compliant notifications, this method
* returns the cause provided when the alarm notification was generated.
* @return the cause of this alarm, or <code>null</code> if no cause was
* provided.
*/
public Throwable getCause() {
return cause;
}
/**
* Enable the serialization of vendor-specific data for objects of this class.
* This method is typically used by a SLEE implementation that wants to export
* vendor-specific data with objects of this class to management clients.
* <p>
* By default, any vendor-specific data included in an object of this class will
* not be included in the serialization stream when the object is serialized.
* Invoking this method changes this behavior so that vendor-specific data is
* included in the serialization stream when an object of this class is serialized.
* <p>
* This method should only be invoked if the vendor-specific data is serializable
* via standard Java serialization means.
* @since SLEE 1.1
* @see #disableVendorDataSerialization
* @see #setVendorData
*/
public static void enableVendorDataSerialization() {
vendorDataSerializationEnabled = true;
}
/**
* Disable the serialization of vendor-specific data for objects of this class.
* <p>
* If the serialization of vendor-specific data for objects of this class has
* been enabled via the {@link #enableVendorDataSerialization} method, this
* method disables that behavior again.
* @since SLEE 1.1
*/
public static void disableVendorDataSerialization() {
vendorDataSerializationEnabled = false;
}
/**
* Enable the deserialization of vendor-specific data for objects of this class.
* This method is typically used by a management client that wants to obtain any
* vendor-specific data included in the serialization stream of objects of this
* class.
* <p>
* By default, any vendor-specific data included in the serialization stream of
* objects of this class is discarded upon deserialization. Invoking this method
* changes that behavior so that the vendor-specific data is also deserialized
* when an object of this class is deserialized. A management client that enables
* the deserialization of vendor-specific data must ensure that any necessary
* classes required to deserialize that data is available in the relevant
* classloader.
* @since SLEE 1.1
* @see #disableVendorDataDeserialization
* @see #getVendorData
*/
public static void enableVendorDataDeserialization() {
vendorDataDeserializationEnabled = true;
}
/**
* Disable the deserialization of vendor-specific data for objects of this class.
* <p>
* If the deserialization of vendor-specific data for objects of this class has
* been enabled via the {@link #enableVendorDataDeserialization} method, this
* method disables that behavior again.
* @since SLEE 1.1
*/
public static void disableVendorDataDeserialization() {
vendorDataDeserializationEnabled = false;
}
/**
* Set the vendor-specific data.
* @param vendorData the vendor-specific data.
* @since SLEE 1.1
*/
public void setVendorData(Object vendorData) {
this.vendorData = vendorData;
}
/**
* Get the vendor-specific data.
* @return the vendor-specific data.
* @since SLEE 1.1
*/
public Object getVendorData() {
return vendorData;
}
/**
* Compare this notification for equality with another object.
* <p>
* For backwards compatibility, this method performs either a SLEE 1.0-based comparison or
* a SLEE 1.1-based comparison based on the state of this notification object. If this
* notification contains a non-null alarm source reference, a SLEE 1.0-based comparison
* is performed, using the SLEE 1.0 alarm levels. Otherwise, a SLEE 1.1-based comparison
* is performed using the SLEE 1.1 alarm levels.
* <p>
* The SLEE 1.0-based comparison considers two notifications to be equal if <code>obj</code>
* is an instance of this class and the alarm type, alarm source, SLEE 1.0 alarm level, and
* message attributes of <code>obj</code> are the same as the corresponding attributes of
* <code>this</code>.
* <p>
* The SLEE 1.1-based comparison considers two notifications to be equal if <code>obj</code>
* if an instance of this class and the alarm identifier, notification source, alarm type,
* alarm instance ID, SLEE 1.1 alarm level, and message attributes of <code>obj</code> are
* the same as the corresponding attributes of <code>this</code>.
* <p>
* Note that a SLEE 1.0-compliant alarm notification can never be equal to a SLEE 1.1-compliant
* alarm notification.
* @param obj the object to compare this with.
* @return <code>true</code> if <code>obj</code> is equal to <code>this</code> according to
* the rules specified above, <code>false</code> otherwise.
*/
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof AlarmNotification)) return false;
AlarmNotification that = (AlarmNotification)obj;
if (this.notificationSource == null) {
// SLEE 1.0
// is 'that' a SLEE 1.0 notification?
if (that.notificationSource != null) return false;
return (this.alarmType.equals(that.alarmType))
&& (this.alarmSource.equals(that.alarmSource))
&& (this.level_10.equals(that.level_10))
&& (this.getMessage().equals(that.getMessage()));
}
else {
// SLEE 1.1
// is that a SLEE 1.1 notification?
if (that.notificationSource == null) return false;
return (this.alarmID.equals(that.alarmID))
&& (this.notificationSource.equals(that.notificationSource))
&& (this.alarmType.equals(that.alarmType))
&& (this.instanceID.equals(that.instanceID))
&& (this.level_11.equals(that.level_11))
&& (this.getMessage().equals(that.getMessage()));
}
}
/**
* Get a hash code value for this notification. For SLEE 1.1-compliant alarm
* notifications this is the hash code of the unique alarm identifier. For
* SLEE 1.0-compliant notifications this is the hash code of the notification's
* message.
* @return a hash code for this notification.
*/
public int hashCode() {
return (this.level_10 != null) ? getMessage().hashCode() : alarmID.hashCode();
}
/**
* Get a string representation for this notification.
* @return a string representation for this notification.
*/
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("AlarmNotification[");
if (notificationSource == null) {
// generate a SLEE 1.0 message
buf.append("type=").append(getType()).
append(",timestamp=").append(getTimeStamp()).
append(",alarmType=").append(alarmType).
append(",source=").append(alarmSource).
append(",level=").append(level_10).
append(",message=").append(getMessage()).
append(",cause=").append(cause);
}
else {
// generate a SLEE 1.1 message
buf.append("type=").append(getType()).
append(",id=").append(alarmID).
append(",source=").append(notificationSource).
append(",alarmType=").append(alarmType).
append(",instanceID=").append(instanceID).
append(",level=").append(level_11).
append(",message=").append(getMessage()).
append(",cause=").append(cause).
append(",timestamp=").append(getTimeStamp());
}
if (vendorData != null) buf.append(",vendor data=").append(vendorData);
buf.append("]");
return buf.toString();
}
// special handling of serialization
private void writeObject(ObjectOutputStream out) throws IOException {
VendorExtensionUtils.writeObject(out, vendorDataSerializationEnabled ? vendorData : null);
if (cause != null) {
out.writeBoolean(true);
// serialize the cause inside a marshalled object to isolate
// the serialized data for it in the stream
out.writeObject(new MarshalledObject(cause));
// serialize a text form of the stack trace
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
cause.printStackTrace(pw);
pw.flush();
out.writeUTF(sw.getBuffer().toString());
}
else {
out.writeBoolean(false);
}
}
// special handling of deserialization
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
vendorData = VendorExtensionUtils.readObject(in,vendorDataDeserializationEnabled);
if (in.readBoolean()) {
// attempt to deserialize the cause
try {
cause = (Throwable)((MarshalledObject)in.readObject()).get();
}
catch (ClassNotFoundException cnfe) {
// must have been a class not in standard classloaders and not remotely loadable
// do nothing now, we'll replace it with the string version included next in the stream
}
String causeString = in.readUTF();
if (cause == null) {
// replace the cause with a generic exception
// that includes the original stack trace in its message
cause = new Exception("Undeserializable cause, original cause stack trace follows: " + causeString);
}
}
}
private final String alarmID;
private final NotificationSource notificationSource;
private final String alarmType;
private final String instanceID;
private final Object alarmSource;
private final Level level_10;
private final AlarmLevel level_11;
private transient Throwable cause;
private static volatile boolean vendorDataSerializationEnabled = false;
private static volatile boolean vendorDataDeserializationEnabled = false;
private transient Object vendorData;
}