package javax.slee.management; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.StringWriter; import java.io.PrintWriter; import java.rmi.MarshalledObject; import javax.slee.facilities.AlarmLevel; /** * The <code>Alarm</code> class contains information about an alarm that has * been raised in the SLEE. * <p> * Serialization of this class takes into account cause Throwable 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 file:// URL on a different host that cannot be resolved on the client * host). Serialization of an <code>Alarm</code> object containing a cause includes the * stack trace of that cause in the serialization stream. If the cause <code>Throwable</code> * cannot be later deserialized with the <code>Alarm</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. * @since SLEE 1.1 */ public final class Alarm implements VendorExtensions, Serializable, Comparable { /** * Create an <code>Alarm</code> object that contains information about an * alarm that has been raised in the SLEE. * @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. * @param level the alarm level of the alarm. * @param message the reason for the alarm. * @param cause an optional cause throwable for the alarm. May be <code>null</code> * if no cause was provided when the alarm was raised. * @param timestamp the time (in ms since January 1, 1970 UTC) that the alarm * was raised. * @throws NullPointerException if <code>alarmID</code>, <code>notificationSource</code>, * <code>alarmType</code>, <code>instanceID</code>, <code>level</code>, * or <code>message</code> is <code>null</code>. */ public Alarm(String alarmID, NotificationSource notificationSource, String alarmType, String instanceID, AlarmLevel level, String message, Throwable cause, long timestamp) throws NullPointerException { 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 (level == null) throw new NullPointerException("level 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 = level; this.message = message; this.cause = cause; this.timestamp = timestamp; } /** * Get the unique alarm identifier for the alarm. * @return the alarm identifier. */ 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. */ public NotificationSource getNotificationSource() { return notificationSource; } /** * Get the identifier specifying the type of the alarm, provided when the * alarm was raised. * @return the type of the alarm. */ public String getAlarmType() { return alarmType; } /** * Get the identifier specifying the particular instance of the alarm type, * provided when the alarm was raised. * @return the alarm instance ID. */ public String getInstanceID() { return instanceID; } /** * Get the alarm level of the alarm. * @return the alarm level. */ public AlarmLevel getAlarmLevel() { return level; } /** * Get the message provided when the alarm was raised. * @return the alarm message. */ public String getMessage() { return message; } /** * Get the cause provided when the alarm was raised (if any). * @return the cause of the alarm, or <code>null</code> if no cause was provided. */ public Throwable getCause() { return cause; } /** * Get the time (in ms since January 1, 1970 UTC) that the alarm was raised. * @return the time that the alarm was raised. */ public long getTimestamp() { return timestamp; } /** * 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. * @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. */ 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. * @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. */ public static void disableVendorDataDeserialization() { vendorDataDeserializationEnabled = false; } /** * Set the vendor-specific data. * @param vendorData the vendor-specific data. */ public void setVendorData(Object vendorData) { this.vendorData = vendorData; } /** * Get the vendor-specific data. * @return the vendor-specific data. */ public Object getVendorData() { return vendorData; } /** * Compare this alarm for equality with another object. * @param obj the object to compare this with. * @return <code>true</code> if <code>obj</code> is an alarm with the same * notification source, alarm type, and alarm instance ID as this, * <code>false</code> otherwise. * @see Object#equals(Object) */ public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof Alarm)) return false; Alarm that = (Alarm)obj; return this.notificationSource.equals(that.notificationSource) && this.alarmType.equals(that.alarmType) && this.instanceID.equals(that.instanceID); } /** * Get a hash code value for this alarm. The hash code is calculated from the * exclusive-or of the notification source, alarm type, and alarm instance ID. * @return a hash code value for this alarm. * @see Object#hashCode() */ public int hashCode() { return notificationSource.hashCode() ^ alarmType.hashCode() ^ instanceID.hashCode(); } /** * Compare this alarm with the specified object for order. * Returns a negative integer, zero, or a positive integer if this object * is less than, equal to, or greater than the specified object. * <p> * Alarm ordering is determined by comparing unique the alarm identifier. * @param obj the object to compare this with. * @return a negative integer, zero, or a positive integer if this alarm * object is considered less than, equal to, or greater than the * specified object. * @throws ClassCastException if <code>obj</code> is not an instance of this * class. * @see Comparable#compareTo(Object) */ public int compareTo(Object obj) { if (obj == this) return 0; if (!(obj instanceof Alarm)) throw new ClassCastException("Not a javax.slee.management.Alarm: " + obj); Alarm that = (Alarm)obj; // compare alarm identifiers return this.alarmID.compareTo(that.alarmID); } /** * Get a string representation for this alarm. * @see Object#toString() */ public String toString() { StringBuffer buf = new StringBuffer(); buf.append("Alarm[id=").append(alarmID). append(",source=").append(notificationSource). append(",alarmType=").append(alarmType). append(",instanceID=").append(instanceID). append(",level=").append(level). append(",message=").append(message). append(",cause=").append(cause). append(",timestamp=").append(timestamp); 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 AlarmLevel level; private final String message; private final long timestamp; private transient Throwable cause; private static volatile boolean vendorDataSerializationEnabled = false; private static volatile boolean vendorDataDeserializationEnabled = false; private transient Object vendorData; }