8014085: Better serialization support in JMX classes
Reviewed-by: alanb, dfuchs, skoivu
--- a/jdk/src/share/classes/javax/management/MBeanNotificationInfo.java Fri Jun 14 15:49:54 2013 +0100
+++ b/jdk/src/share/classes/javax/management/MBeanNotificationInfo.java Thu Jun 20 08:51:47 2013 +0200
@@ -25,6 +25,9 @@
package javax.management;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.util.Arrays;
/**
@@ -67,7 +70,7 @@
/**
* @serial The different types of the notification.
*/
- private final String[] types;
+ private String[] types;
/** @see MBeanInfo#arrayGettersSafe */
private final transient boolean arrayGettersSafe;
@@ -114,9 +117,8 @@
notifType, though it doesn't explicitly allow it
either. */
- if (notifTypes == null)
- notifTypes = NO_TYPES;
- this.types = notifTypes;
+ this.types = (notifTypes != null && notifTypes.length > 0) ?
+ notifTypes.clone() : NO_TYPES;
this.arrayGettersSafe =
MBeanInfo.arrayGettersSafe(this.getClass(),
MBeanNotificationInfo.class);
@@ -203,4 +205,16 @@
hash ^= types[i].hashCode();
return hash;
}
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gf = ois.readFields();
+ String[] t = (String[])gf.get("types", null);
+
+ if (t == null) {
+ throw new InvalidObjectException("Trying to deserialize an invalid " +
+ "instance of " + MBeanNotificationInfo.class +
+ "[types=null]");
+ }
+ types = t.length == 0 ? t : t.clone();
+ }
}
--- a/jdk/src/share/classes/javax/management/remote/JMXPrincipal.java Fri Jun 14 15:49:54 2013 +0100
+++ b/jdk/src/share/classes/javax/management/remote/JMXPrincipal.java Thu Jun 20 08:51:47 2013 +0200
@@ -26,6 +26,9 @@
package javax.management.remote;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.security.Principal;
@@ -64,9 +67,7 @@
* <code>null</code>.
*/
public JMXPrincipal(String name) {
- if (name == null)
- throw new NullPointerException("illegal null input");
-
+ validate(name);
this.name = name;
}
@@ -130,4 +131,20 @@
public int hashCode() {
return name.hashCode();
}
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gf = ois.readFields();
+ String principalName = (String)gf.get("name", null);
+ try {
+ validate(principalName);
+ this.name = principalName;
+ } catch (NullPointerException e) {
+ throw new InvalidObjectException(e.getMessage());
+ }
+ }
+
+ private static void validate(String name) throws NullPointerException {
+ if (name == null)
+ throw new NullPointerException("illegal null input");
+ }
}
--- a/jdk/src/share/classes/javax/management/remote/JMXServiceURL.java Fri Jun 14 15:49:54 2013 +0100
+++ b/jdk/src/share/classes/javax/management/remote/JMXServiceURL.java Thu Jun 20 08:51:47 2013 +0200
@@ -29,6 +29,9 @@
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.InetAddress;
@@ -297,7 +300,7 @@
If we're given an explicit host name that is illegal we
have to reject it. (Bug 5057532.) */
try {
- validateHost(host);
+ validateHost(host, port);
} catch (MalformedURLException e) {
if (logger.fineOn()) {
logger.fine("JMXServiceURL",
@@ -336,36 +339,82 @@
validate();
}
- private void validate() throws MalformedURLException {
-
- // Check protocol
+ private static final String INVALID_INSTANCE_MSG =
+ "Trying to deserialize an invalid instance of JMXServiceURL";
+ private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gf = inputStream.readFields();
+ String h = (String)gf.get("host", null);
+ int p = (int)gf.get("port", -1);
+ String proto = (String)gf.get("protocol", null);
+ String url = (String)gf.get("urlPath", null);
- final int protoEnd = indexOfFirstNotInSet(protocol, protocolBitSet, 0);
- if (protoEnd == 0 || protoEnd < protocol.length()
- || !alphaBitSet.get(protocol.charAt(0))) {
+ if (proto == null || url == null || h == null) {
+ StringBuilder sb = new StringBuilder(INVALID_INSTANCE_MSG).append('[');
+ boolean empty = true;
+ if (proto == null) {
+ sb.append("protocol=null");
+ empty = false;
+ }
+ if (h == null) {
+ sb.append(empty ? "" : ",").append("host=null");
+ empty = false;
+ }
+ if (url == null) {
+ sb.append(empty ? "" : ",").append("urlPath=null");
+ }
+ sb.append(']');
+ throw new InvalidObjectException(sb.toString());
+ }
+
+ if (h.contains("[") || h.contains("]")) {
+ throw new InvalidObjectException("Invalid host name: " + h);
+ }
+
+ try {
+ validate(proto, h, p, url);
+ this.protocol = proto;
+ this.host = h;
+ this.port = p;
+ this.urlPath = url;
+ } catch (MalformedURLException e) {
+ throw new InvalidObjectException(INVALID_INSTANCE_MSG + ": " +
+ e.getMessage());
+ }
+
+ }
+
+ private void validate(String proto, String h, int p, String url)
+ throws MalformedURLException {
+ // Check protocol
+ final int protoEnd = indexOfFirstNotInSet(proto, protocolBitSet, 0);
+ if (protoEnd == 0 || protoEnd < proto.length()
+ || !alphaBitSet.get(proto.charAt(0))) {
throw new MalformedURLException("Missing or invalid protocol " +
- "name: \"" + protocol + "\"");
+ "name: \"" + proto + "\"");
}
// Check host
-
- validateHost();
+ validateHost(h, p);
// Check port
-
- if (port < 0)
- throw new MalformedURLException("Bad port: " + port);
+ if (p < 0)
+ throw new MalformedURLException("Bad port: " + p);
// Check URL path
-
- if (urlPath.length() > 0) {
- if (!urlPath.startsWith("/") && !urlPath.startsWith(";"))
- throw new MalformedURLException("Bad URL path: " + urlPath);
+ if (url.length() > 0) {
+ if (!url.startsWith("/") && !url.startsWith(";"))
+ throw new MalformedURLException("Bad URL path: " + url);
}
}
- private void validateHost() throws MalformedURLException {
- if (host.length() == 0) {
+ private void validate() throws MalformedURLException {
+ validate(this.protocol, this.host, this.port, this.urlPath);
+ }
+
+ private static void validateHost(String h, int port)
+ throws MalformedURLException {
+
+ if (h.length() == 0) {
if (port != 0) {
throw new MalformedURLException("Cannot give port number " +
"without host name");
@@ -373,12 +422,6 @@
return;
}
- validateHost(host);
- }
-
- private static void validateHost(String h)
- throws MalformedURLException {
-
if (isNumericIPv6Address(h)) {
/* We assume J2SE >= 1.4 here. Otherwise you can't
use the address anyway. We can't call
@@ -663,22 +706,22 @@
/**
* The value returned by {@link #getProtocol()}.
*/
- private final String protocol;
+ private String protocol;
/**
* The value returned by {@link #getHost()}.
*/
- private final String host;
+ private String host;
/**
* The value returned by {@link #getPort()}.
*/
- private final int port;
+ private int port;
/**
* The value returned by {@link #getURLPath()}.
*/
- private final String urlPath;
+ private String urlPath;
/**
* Cached result of {@link #toString()}.
--- a/jdk/src/share/classes/javax/management/remote/NotificationResult.java Fri Jun 14 15:49:54 2013 +0100
+++ b/jdk/src/share/classes/javax/management/remote/NotificationResult.java Thu Jun 20 08:51:47 2013 +0200
@@ -25,6 +25,9 @@
package javax.management.remote;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
/**
@@ -76,17 +79,7 @@
public NotificationResult(long earliestSequenceNumber,
long nextSequenceNumber,
TargetedNotification[] targetedNotifications) {
- if (targetedNotifications == null) {
- final String msg = "Notifications null";
- throw new IllegalArgumentException(msg);
- }
-
- if (earliestSequenceNumber < 0 || nextSequenceNumber < 0)
- throw new IllegalArgumentException("Bad sequence numbers");
- /* We used to check nextSequenceNumber >= earliestSequenceNumber
- here. But in fact the opposite can legitimately be true if
- notifications have been lost. */
-
+ validate(targetedNotifications, earliestSequenceNumber, nextSequenceNumber);
this.earliestSequenceNumber = earliestSequenceNumber;
this.nextSequenceNumber = nextSequenceNumber;
this.targetedNotifications = (targetedNotifications.length == 0 ? targetedNotifications : targetedNotifications.clone());
@@ -138,7 +131,39 @@
getTargetedNotifications().length;
}
- private final long earliestSequenceNumber;
- private final long nextSequenceNumber;
- private final TargetedNotification[] targetedNotifications;
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gf = ois.readFields();
+ TargetedNotification[] tNotifs = (TargetedNotification[])gf.get("targetedNotifications", null);
+ long snStart = gf.get("earliestSequenceNumber", -1L);
+ long snNext = gf.get("nextSequenceNumber", -1L);
+ try {
+ validate(tNotifs, snStart, snNext);
+
+ this.targetedNotifications = tNotifs.length == 0 ? tNotifs : tNotifs.clone();
+ this.earliestSequenceNumber = snStart;
+ this.nextSequenceNumber = snNext;
+ } catch (IllegalArgumentException e) {
+ throw new InvalidObjectException(e.getMessage());
+ }
+ }
+
+ private long earliestSequenceNumber;
+ private long nextSequenceNumber;
+ private TargetedNotification[] targetedNotifications;
+
+ private static void validate(TargetedNotification[] targetedNotifications,
+ long earliestSequenceNumber,
+ long nextSequenceNumber)
+ throws IllegalArgumentException {
+ if (targetedNotifications == null) {
+ final String msg = "Notifications null";
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (earliestSequenceNumber < 0 || nextSequenceNumber < 0)
+ throw new IllegalArgumentException("Bad sequence numbers");
+ /* We used to check nextSequenceNumber >= earliestSequenceNumber
+ here. But in fact the opposite can legitimately be true if
+ notifications have been lost. */
+ }
}
--- a/jdk/src/share/classes/javax/management/remote/TargetedNotification.java Fri Jun 14 15:49:54 2013 +0100
+++ b/jdk/src/share/classes/javax/management/remote/TargetedNotification.java Thu Jun 20 08:51:47 2013 +0200
@@ -26,6 +26,9 @@
package javax.management.remote;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import javax.management.Notification;
@@ -73,12 +76,9 @@
*/
public TargetedNotification(Notification notification,
Integer listenerID) {
+ validate(notification, listenerID);
// If we replace integer with int...
// this(notification,intValue(listenerID));
- if (notification == null) throw new
- IllegalArgumentException("Invalid notification: null");
- if (listenerID == null) throw new
- IllegalArgumentException("Invalid listener ID: null");
this.notif = notification;
this.id = listenerID;
}
@@ -115,13 +115,13 @@
* @serial A notification to transmit to the other side.
* @see #getNotification()
**/
- private final Notification notif;
+ private Notification notif;
/**
* @serial The ID of the listener to which the notification is
* targeted.
* @see #getListenerID()
**/
- private final Integer id;
+ private Integer id;
//private final int id;
// Needed if we use int instead of Integer...
@@ -130,4 +130,26 @@
// IllegalArgumentException("Invalid listener ID: null");
// return id.intValue();
// }
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gf = ois.readFields();
+ Notification notification = (Notification)gf.get("notif", null);
+ Integer listenerId = (Integer)gf.get("id", null);
+ try {
+ validate(notification, listenerId);
+ this.notif = notification;
+ this.id = listenerId;
+ } catch (IllegalArgumentException e) {
+ throw new InvalidObjectException(e.getMessage());
+ }
+ }
+
+ private static void validate(Notification notif, Integer id) throws IllegalArgumentException {
+ if (notif == null) {
+ throw new IllegalArgumentException("Invalid notification: null");
+ }
+ if (id == null) {
+ throw new IllegalArgumentException("Invalid listener ID: null");
+ }
+ }
}