/* Copyright (c) Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.ComponentModel; using XenAdmin.Network; using XenAPI; namespace XenAdmin { public class OtherConfigAndTagsWatcher { public static event Action OtherConfigChanged; public static event Action TagsChanged; public static event Action GuiConfigChanged; private static bool FireOtherConfigEvent; private static bool FireTagsEvent; private static bool FireGuiConfigEvent; static OtherConfigAndTagsWatcher() { ConnectionsManager.XenConnections.CollectionChanged += XenConnections_CollectionChanged; MarkEventsReadyToFire(true); } public static void DeregisterEventHandlers() { ConnectionsManager.XenConnections.CollectionChanged -= XenConnections_CollectionChanged; foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy) { connection.Cache.DeregisterCollectionChanged(PoolCollectionChangedWithInvoke); connection.Cache.DeregisterCollectionChanged(HostCollectionChangedWithInvoke); connection.Cache.DeregisterCollectionChanged(VMCollectionChangedWithInvoke); connection.Cache.DeregisterCollectionChanged(SRCollectionChangedWithInvoke); connection.Cache.DeregisterCollectionChanged(VDICollectionChangedWithInvoke); connection.Cache.DeregisterCollectionChanged(NetworkCollectionChangedWithInvoke); connection.XenObjectsUpdated -= connection_XenObjectsUpdated; connection.ConnectionStateChanged -= connection_ConnectionStateChanged; } } public static void InitEventHandlers() { PoolCollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); VMCollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); HostCollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); SRCollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); VDICollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); NetworkCollectionChangedWithInvoke = InvokeHelper.InvokeHandler(CollectionChanged); MarkEventsReadyToFire(true); } private static CollectionChangeEventHandler PoolCollectionChangedWithInvoke; private static CollectionChangeEventHandler VMCollectionChangedWithInvoke; private static CollectionChangeEventHandler HostCollectionChangedWithInvoke; private static CollectionChangeEventHandler SRCollectionChangedWithInvoke; private static CollectionChangeEventHandler VDICollectionChangedWithInvoke; private static CollectionChangeEventHandler NetworkCollectionChangedWithInvoke; private static void XenConnections_CollectionChanged(object sender, CollectionChangeEventArgs e) { if (e == null) return; IXenConnection connection = e.Element as IXenConnection; if (e.Action == CollectionChangeAction.Add) { if (connection == null) return; connection.Cache.RegisterCollectionChanged(PoolCollectionChangedWithInvoke); connection.Cache.RegisterCollectionChanged(HostCollectionChangedWithInvoke); connection.Cache.RegisterCollectionChanged(VMCollectionChangedWithInvoke); connection.Cache.RegisterCollectionChanged(SRCollectionChangedWithInvoke); connection.Cache.RegisterCollectionChanged(VDICollectionChangedWithInvoke); connection.Cache.RegisterCollectionChanged(NetworkCollectionChangedWithInvoke); connection.XenObjectsUpdated -= connection_XenObjectsUpdated; connection.XenObjectsUpdated += connection_XenObjectsUpdated; connection.ConnectionStateChanged -= connection_ConnectionStateChanged; connection.ConnectionStateChanged += connection_ConnectionStateChanged; } else if (e.Action == CollectionChangeAction.Remove) { var range = new List(); if (connection != null) { range.Add(connection); } else { var r = e.Element as List; if (r != null) range = r; else return; } foreach (var con in range) { con.Cache.DeregisterCollectionChanged(PoolCollectionChangedWithInvoke); con.Cache.DeregisterCollectionChanged(HostCollectionChangedWithInvoke); con.Cache.DeregisterCollectionChanged(VMCollectionChangedWithInvoke); con.Cache.DeregisterCollectionChanged(SRCollectionChangedWithInvoke); con.Cache.DeregisterCollectionChanged(VDICollectionChangedWithInvoke); con.Cache.DeregisterCollectionChanged(NetworkCollectionChangedWithInvoke); con.XenObjectsUpdated -= connection_XenObjectsUpdated; con.ConnectionStateChanged -= connection_ConnectionStateChanged; } } MarkEventsReadyToFire(true); } private static void CollectionChanged(object sender, CollectionChangeEventArgs e) where T : XenObject { T xmo = e.Element as T; if (xmo == null) return; switch (e.Action) { case CollectionChangeAction.Add: xmo.PropertyChanged += PropertyChanged; break; case CollectionChangeAction.Remove: xmo.PropertyChanged -= PropertyChanged; break; case CollectionChangeAction.Refresh: // As of writing, ChangeableDictionary never fires a Refresh event. // If this changes, we need to take it into account here. throw new NotImplementedException("CollectionChangeAction.Refresh is unhandled!"); } MarkEventsReadyToFire(true); } private static void PropertyChanged(object sender1, PropertyChangedEventArgs e) where T : XenObject { if (e.PropertyName == "other_config") FireOtherConfigEvent = true; if (e.PropertyName == "tags") FireTagsEvent = true; if (e.PropertyName == "gui_config") FireGuiConfigEvent = true; } private static void connection_XenObjectsUpdated(object sender, EventArgs e) { if (FireOtherConfigEvent) OnOtherConfigChanged(); if (FireTagsEvent) OnTagsChanged(); if (FireGuiConfigEvent) OnGuiConfigChanged(); MarkEventsReadyToFire(false); } private static void connection_ConnectionStateChanged(object sender, EventArgs e) { InvokeHelper.Invoke(delegate { OnOtherConfigChanged(); OnTagsChanged(); OnGuiConfigChanged(); MarkEventsReadyToFire(false); }); } private static void MarkEventsReadyToFire(bool fire) { FireOtherConfigEvent = fire; FireTagsEvent = fire; FireGuiConfigEvent = fire; } private static void OnOtherConfigChanged() { InvokeHelper.AssertOnEventThread(); if (OtherConfigChanged != null) OtherConfigChanged(); } private static void OnTagsChanged() { InvokeHelper.AssertOnEventThread(); if (TagsChanged != null) TagsChanged(); } private static void OnGuiConfigChanged() { InvokeHelper.AssertOnEventThread(); if (GuiConfigChanged != null) GuiConfigChanged(); } } }