/* 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 XenAdmin.Network; using XenAPI; using System.Reflection; namespace XenAdmin.Actions { /// /// Saves the changes on the given IXenModelObjects /// public class SaveChangesAction : PureAsyncAction { private readonly IXenObject _xenObject = null; private readonly IXenObject _serverXenObject = null; private readonly IXenObject _beforeObject = null; // In 5.5, we used to lock the object being edited in the RunAsync() action. We now lock it in the // constructor to avoid a race, but we still throw the exception for trying to lock a locked object // in Run() (it no longer goes through RunAsync()) to avoid reworking all the calling code. private bool lockViolation = false; protected SaveChangesAction(IXenObject obj) :this(obj, false) {} protected SaveChangesAction(IXenObject obj, bool suppressHistory) : base(obj.Connection, Messages.ACTION_SAVE_CHANGES_TITLE, Messages.ACTION_SAVE_CHANGES_IN_PROGRESS, suppressHistory) { // This is lovely. We need to lock the server object (not the copy we have taken) before calling save changes. // We don't know the type so we use the MethodInfo object and MakeGenericMethod to do a resolve against the type // we have extracted from GetType(). The Resolve() call itself needs a XenRef which we make by calling the XenRef constructor that takes the // opaque ref as an argument and using the MakeGenericType call to give it the same type as the object... // ... _then_ we lock it. SetObject(obj); _xenObject = obj; if (obj.opaque_ref != null) // creating a new object comes through here, but with obj.opaque_ref == null { System.Reflection.MethodInfo mi = typeof(IXenConnection).GetMethod("Resolve", BindingFlags.Public | BindingFlags.Instance); Type type = obj.GetType(); object[] xenRefParam = new object[] { typeof(XenRef<>).MakeGenericType(type).GetConstructor(new Type[] {typeof(string)}).Invoke(new Object[] {obj.opaque_ref}) }; _serverXenObject = (IXenObject)mi.MakeGenericMethod(type).Invoke(obj.Connection, xenRefParam); if (_serverXenObject != null) { // CA-35210: Removed this exception pending locking overhaul post MR in CA-38966 //if (_serverXenObject.Locked) // lockViolation = true; _serverXenObject.Locked = true; } } } public SaveChangesAction(IXenObject obj, IXenObject beforeObject, bool suppressHistory) : this(obj, suppressHistory) { _beforeObject = beforeObject; } protected override void Run() { if (lockViolation) throw new InvalidOperationException(Messages.SAVECHANGES_LOCKED); _xenObject.SaveChanges(Session, _beforeObject); Description = Messages.ACTION_SAVE_CHANGES_SUCCESSFUL; } protected override void Clean() { if (_serverXenObject != null) _serverXenObject.Locked = false; } } }