components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/common/ConnectionTracker.java
changeset 827 0944d8c0158b
child 1410 ca9946e5736c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/common/ConnectionTracker.java	Thu May 24 04:16:47 2012 -0400
@@ -0,0 +1,523 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package com.oracle.solaris.vp.panel.common;
+
+import java.beans.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.*;
+import javax.management.*;
+import com.oracle.solaris.vp.util.misc.ObjectUtil;
+import com.oracle.solaris.vp.util.misc.event.PropertyChangeListeners;
+
+/**
+ * The {@code ConnectionTracker} class manages listeners that depend on
+ * connections.
+ * </p>
+ * {@code ConnectionListener}s that depend on a {@link ClientContext} are
+ * automatically removed/added when the tracked {@code ClientContext}
+ * fails/changes.
+ * </p>
+ * {@code NotificationListener}s that depend on an {@code MBeanServerConnection}
+ * are automatically removed/added when the tracked {@code
+ * MBeanServerConnection} or {@code ObjectName} changes.
+ * </p>
+ * Property change notifications are sent out when the tracked {@code
+ * ClientContext}, {@code ConnectionInfo}, {@code MBeanServerConnection}, or
+ * {@code ObjectName} changes.
+ */
+public class ConnectionTracker implements ConnectionListener {
+    //
+    // Inner classes
+    //
+
+    private abstract class NotifyParams<O> {
+	private O listener;
+	private NotificationFilter filter;
+	private Object handback;
+
+	//
+	// Constructors
+	//
+
+	public NotifyParams(O listener, NotificationFilter filter,
+	    Object handback) {
+
+	    this.listener = listener;
+	    this.filter = filter;
+	    this.handback = handback;
+	}
+
+	//
+	// NotifyParams methods
+	//
+
+	public boolean equals(Object listener) {
+	    return ObjectUtil.equals(this.listener, listener);
+	}
+
+	public boolean equals(Object listener, NotificationFilter filter,
+	    Object handback) {
+
+	    return ObjectUtil.equals(this.listener, listener) &&
+		ObjectUtil.equals(this.filter, filter) &&
+		ObjectUtil.equals(this.handback, handback);
+	}
+
+	public O getListener() {
+	    return listener;
+	}
+
+	public NotificationFilter getFilter() {
+	    return filter;
+	}
+
+	public Object getHandback() {
+	    return handback;
+	}
+
+	public abstract void add()
+	    throws InstanceNotFoundException, IOException;
+
+	public abstract void remove()
+	    throws InstanceNotFoundException, IOException;
+    }
+
+    private class ListenerNotifyParams
+	extends NotifyParams<NotificationListener> {
+	//
+	// Constructors
+	//
+
+	public ListenerNotifyParams(NotificationListener listener,
+	    NotificationFilter filter, Object handback) {
+	    super(listener, filter, handback);
+	}
+
+	//
+	// ListenerNotifyParams methods
+	//
+
+	@Override
+	public void add() throws InstanceNotFoundException, IOException {
+	    if (mbsc != null && oName != null) {
+		mbsc.addNotificationListener(oName, getListener(), getFilter(),
+		    getHandback());
+	    }
+	}
+
+	@Override
+	public void remove() throws InstanceNotFoundException,
+	    IOException {
+
+	    if (mbsc != null && oName != null) {
+		try {
+		    mbsc.removeNotificationListener(oName, getListener(),
+			getFilter(), getHandback());
+		} catch (ListenerNotFoundException ignore) {
+		}
+	    }
+	}
+    }
+
+    private class ObjectNameNotifyParams extends NotifyParams<ObjectName> {
+	//
+	// Constructors
+	//
+
+	public ObjectNameNotifyParams(ObjectName listener,
+	    NotificationFilter filter, Object handback) {
+	    super(listener, filter, handback);
+	}
+
+	//
+	// ObjectNameNotifyParams methods
+	//
+
+	@Override
+	public void add() throws InstanceNotFoundException, IOException {
+	    if (mbsc != null && oName != null) {
+		mbsc.addNotificationListener(oName, getListener(), getFilter(),
+		    getHandback());
+	    }
+	}
+
+	@Override
+	public void remove() throws InstanceNotFoundException,
+	    IOException {
+
+	    if (mbsc != null && oName != null) {
+		try {
+		    mbsc.removeNotificationListener(oName, getListener(),
+			getFilter(), getHandback());
+		} catch (ListenerNotFoundException ignore) {
+		}
+	    }
+	}
+    }
+
+    //
+    // Static data
+    //
+
+    /**
+     * The name of the property that changes with {@link #setClientContext}.
+     */
+    public static final String PROPERTY_CONTEXT = "context";
+
+    /**
+     * The name of the property that changes with {@link #setConnectionInfo}.
+     */
+    public static final String PROPERTY_INFO = "info";
+
+    /**
+     * The name of the property that changes with {@link
+     * #setMBeanServerConnection}.
+     */
+    public static final String PROPERTY_MBSC = "mbsc";
+
+    /**
+     * The name of the property that changes with {@link #setObjectName}.
+     */
+    public static final String PROPERTY_OBJECTNAME = "objectname";
+
+    //
+    // Instance data
+    //
+
+    private ClientContext context;
+    private ConnectionInfo info;
+    private MBeanServerConnection mbsc;
+    private ObjectName oName;
+    private PropertyChangeListeners pListeners = new PropertyChangeListeners();
+    private List<NotifyParams> nListeners = new LinkedList<NotifyParams>();
+
+    //
+    // Constructors
+    //
+
+    public ConnectionTracker(ObjectName oName) {
+	try {
+	    setObjectName(oName);
+
+	// Impossible because no NotificationListeners have been added yet
+	} catch (TrackerException impossible) {
+	}
+    }
+
+    public ConnectionTracker(ObjectName oName, ClientContext context)
+	throws TrackerException {
+
+	this(oName);
+	setClientContext(context);
+    }
+
+    //
+    // ConnectionListener methods
+    //
+
+    @Override
+    public void connectionChanged(ConnectionEvent event) {
+	ConnectionInfo info = event.getConnectionInfo();
+
+	try {
+	    setConnectionInfo(info);
+	} catch (TrackerException e) {
+	    Logger.getLogger(getClass().getName()).log(
+		Level.SEVERE, "could not communicate with remote host", e);
+	}
+    }
+
+    @Override
+    public void connectionFailed(ConnectionEvent event) {
+	try {
+	    setConnectionInfo(null);
+	} catch (Throwable ignore) {
+	}
+    }
+
+    //
+    // ConnectionTracker methods
+    //
+
+    public void addNotificationListener(NotificationListener listener,
+	NotificationFilter filter, Object handback)
+	throws InstanceNotFoundException, IOException {
+
+	NotifyParams params = new ListenerNotifyParams(listener, filter,
+	    handback);
+
+	params.add();
+	nListeners.add(params);
+    }
+
+    public void addNotificationListener(ObjectName listener,
+	NotificationFilter filter, Object handback)
+	throws InstanceNotFoundException, IOException {
+
+	NotifyParams params = new ObjectNameNotifyParams(listener, filter,
+	    handback);
+
+	params.add();
+	nListeners.add(params);
+    }
+
+    public void addPropertyChangeListener(String propName,
+	PropertyChangeListener listener) {
+	pListeners.add(propName, listener);
+    }
+
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+	pListeners.add(listener);
+    }
+
+    /**
+     * Sets the {@link #getClientContext ClientContext} to {@code null} and
+     * removes all listeners, ignoring any exceptions along the way.
+     */
+    public void dispose() {
+	try {
+	    setClientContext(null);
+
+	// If something prevented us from removing a notification listener, it
+	// probably doesn't matter anymore.
+	} catch (TrackerException ignore) {
+	}
+
+	pListeners.clear();
+	nListeners.clear();
+    }
+
+    public ClientContext getClientContext() {
+	return context;
+    }
+
+    public ConnectionInfo getConnectionInfo() {
+	return info;
+    }
+
+    public MBeanServerConnection getMBeanServerConnection() {
+	return mbsc;
+    }
+
+    public ObjectName getObjectName() {
+	return oName;
+    }
+
+    protected PropertyChangeListeners getPropertyChangeListeners() {
+	return pListeners;
+    }
+
+    public void removeNotificationListener(NotificationListener listener)
+	throws InstanceNotFoundException, IOException {
+
+	for (Iterator<NotifyParams> i = nListeners.iterator(); i.hasNext();) {
+	    NotifyParams params = i.next();
+	    if (params.equals(listener)) {
+		params.remove();
+		i.remove();
+	    }
+	}
+    }
+
+    public void removeNotificationListener(NotificationListener listener,
+	NotificationFilter filter, Object handback)
+	throws InstanceNotFoundException, IOException {
+
+	for (Iterator<NotifyParams> i = nListeners.iterator(); i.hasNext();) {
+	    NotifyParams params = i.next();
+	    if (params.equals(listener, filter, handback)) {
+		params.remove();
+		i.remove();
+		return;
+	    }
+	}
+    }
+
+    public void removeNotificationListener(ObjectName listener)
+	throws InstanceNotFoundException, IOException {
+
+	for (Iterator<NotifyParams> i = nListeners.iterator(); i.hasNext();) {
+	    NotifyParams params = i.next();
+	    if (params.equals(listener)) {
+		params.remove();
+		i.remove();
+	    }
+	}
+    }
+
+    public void removeNotificationListener(ObjectName name, ObjectName listener,
+	NotificationFilter filter, Object handback)
+	throws InstanceNotFoundException, IOException {
+
+	for (Iterator<NotifyParams> i = nListeners.iterator(); i.hasNext();) {
+	    NotifyParams params = i.next();
+	    if (params.equals(listener, filter, handback)) {
+		params.remove();
+		i.remove();
+		return;
+	    }
+	}
+    }
+
+    public void removePropertyChangeListener(String propName,
+	PropertyChangeListener listener) {
+	pListeners.remove(propName, listener);
+    }
+
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+	pListeners.remove(listener);
+    }
+
+    /**
+     * Sets the {@code ClientContext}, then calls {@link #setConnectionInfo}.
+     *
+     * @exception   TrackerException
+     *		    if an error occurred during initialization
+     */
+    public void setClientContext(ClientContext context)
+	throws TrackerException {
+
+	if (this.context != context) {
+	    if (this.context != null) {
+		this.context.removeConnectionListener(this);
+	    }
+
+	    PropertyChangeEvent e = new PropertyChangeEvent(
+		this, PROPERTY_CONTEXT, this.context, context);
+	    this.context = context;
+	    pListeners.propertyChange(e);
+
+	    setConnectionInfo(context == null ? null :
+		context.getConnectionInfo());
+
+	    if (context != null) {
+		context.addConnectionListener(this);
+	    }
+	}
+    }
+
+    /**
+     * Sets the {@code ConnectionInfo}, then calls {@link
+     * #setMBeanServerConnection}.
+     *
+     * @exception   TrackerException
+     *		    if an error occurred during initialization
+     */
+    public void setConnectionInfo(ConnectionInfo info)
+	throws TrackerException {
+
+	if (this.info != info) {
+	    PropertyChangeEvent e = new PropertyChangeEvent(
+		this, PROPERTY_INFO, this.info, info);
+	    this.info = info;
+	    pListeners.propertyChange(e);
+
+	    try {
+		setMBeanServerConnection(info == null ? null :
+		    info.getConnector().getMBeanServerConnection());
+	    } catch (IOException ex) {
+		throw new TrackerException(ex);
+	    }
+	}
+    }
+
+    /**
+     * Sets the {@code MBeanServerConnection}.
+     *
+     * @exception   TrackerException
+     *		    if an error occurred during initialization
+     */
+    public void setMBeanServerConnection(MBeanServerConnection mbsc)
+	throws TrackerException {
+
+	if (this.mbsc != mbsc) {
+	    removeNotificationListeners();
+
+	    PropertyChangeEvent e = new PropertyChangeEvent(
+		this, PROPERTY_MBSC, this.mbsc, mbsc);
+	    this.mbsc = mbsc;
+	    pListeners.propertyChange(e);
+
+	    addNotificationListeners();
+	}
+    }
+
+    /**
+     * Sets the {@code ObjectName}.
+     *
+     * @exception   TrackerException
+     *		    if an error occurred during initialization
+     */
+    public void setObjectName(ObjectName oName)
+	throws TrackerException {
+
+	if (!ObjectUtil.equals(this.oName, oName)) {
+	    removeNotificationListeners();
+
+	    PropertyChangeEvent e = new PropertyChangeEvent(
+		this, PROPERTY_OBJECTNAME, this.oName, oName);
+	    this.oName = oName;
+	    pListeners.propertyChange(e);
+
+	    addNotificationListeners();
+	}
+    }
+
+    //
+    // Private methods
+    //
+
+    private void addNotificationListeners()
+	throws TrackerException {
+
+	if (mbsc != null && oName != null) {
+	    for (NotifyParams params : nListeners) {
+		try {
+		    params.add();
+		} catch (InstanceNotFoundException e) {
+		    throw new TrackerException(e);
+		} catch (IOException e) {
+		    throw new TrackerException(e);
+		}
+	    }
+	}
+    }
+
+    private void removeNotificationListeners() {
+	if (mbsc != null && oName != null) {
+	    for (NotifyParams params : nListeners) {
+		try {
+		    params.remove();
+
+		// If something prevented us from removing a notification
+		// listener, it probably doesn't matter anymore.
+		} catch (Throwable ignore) {
+		}
+	    }
+	}
+    }
+}