using System; using System.Collections; using System.Xml; namespace Repository { /// ///

Describes Repository subscribers.
/// Serialized in subscribers.xml.

///

Copyright (c) 2002 Alexis Grandemange
/// Mail: alexis.grandemange@pagebox.net

///
This program is free software; you can redistribute it and/or
	/// modify it under the terms of the GNU Lesser General Public
	/// License as published by the Free Software Foundation; version
	/// 2.1 of the License.
	/// This library is distributed in the hope that it will be useful,
	/// but WITHOUT ANY WARRANTY; without even the implied warranty of
	/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	/// GNU Lesser General Public License for more details.
	/// A copy of the GNU Lesser General Public License lesser.txt should be
	/// included in the distribution.
///
public class RepoSubs : Deployer { /// ///

Subscriber array.
/// Key: subscriber URL (http://...)
/// Value: Subscriber object

///
public Hashtable subscribers = null; private Hashtable internalSubs = new Hashtable(); /// ///

Archive subscriber array.
/// Key: subscriber URL (http://...)
/// Value: Subscriber object

///
public Hashtable asubscribers = null; private Hashtable internalAsubs = new Hashtable(); /// /// This URL ../download /// string downloadURL; /// /// Archive repository (ArchiveRep class). /// RepoArchs archRep; /// /// Dirty bit /// public bool dirty = false; /// /// Creation of a new Subscriber repository. /// public RepoSubs() { subscribers = Hashtable.Synchronized(internalSubs); asubscribers = Hashtable.Synchronized(internalAsubs); } /// /// Subscriber repository initialization. /// /// Download URL /// Archive repository public void init(string du, RepoArchs ra) { downloadURL = du; archRep = ra; } /// /// Unserialization of an existing Subscriber repository. /// /// XML stream to unserialize public RepoSubs(XmlReader r) { r.ReadStartElement("RepoSubs"); restoreSubs(internalSubs, r, "subscribers"); restoreSubs(internalAsubs, r, "asubscribers"); subscribers = Hashtable.Synchronized(internalSubs); asubscribers = Hashtable.Synchronized(internalAsubs); } /// /// Restore subscribers or archive subscribers. /// Invoked in the unsubscriber constructor. /// /// Subscriber table to restore /// XML stream to unserialize /// Element to process private void restoreSubs(Hashtable h, XmlReader r, string elt) { r.ReadStartElement(elt); if (r.NodeType == XmlNodeType.EndElement) return; while (true) { if (r.NodeType == XmlNodeType.Element) { if (r.LocalName.Equals("entry")) { r.Read(); string url = r.ReadElementString("url"); Subscriber s = new Subscriber(r); h.Add(url, s); } else return; } if ((r.NodeType == XmlNodeType.EndElement) && r.LocalName.Equals(elt)) break; if (r.NodeType == XmlNodeType.None) return; r.Read(); } r.ReadEndElement(); } /// /// Serializer. /// /// stream where the Repository subscribers must be serialized public void WriteXml(XmlWriter w) { w.WriteStartElement("RepoSubs"); w.WriteStartElement("subscribers"); IDictionaryEnumerator ide = internalSubs.GetEnumerator(); while(ide.MoveNext()) { w.WriteStartElement("entry"); w.WriteElementString("url", (string)ide.Key); Subscriber s = (Subscriber)ide.Value; s.WriteXml(w); w.WriteEndElement(); } w.WriteEndElement(); w.WriteStartElement("asubscribers"); ide = internalAsubs.GetEnumerator(); while(ide.MoveNext()) { w.WriteStartElement("entry"); w.WriteElementString("url", (string)ide.Key); Subscriber s = (Subscriber)ide.Value; s.WriteXml(w); w.WriteEndElement(); } w.WriteEndElement(); w.WriteEndElement(); dirty = false; } /// /// Adds a subscriber. /// /// status /// user who issued the command /// host which issued the command /// URL of the subscriber public string subscribe(string user, string host, string url) { lock(this) { if (subscribers.ContainsKey(url)) return url + " already subscribed"; IDictionaryEnumerator ide = archRep.archives.GetEnumerator(); string d = null; Hashtable archs = new Hashtable(); while(ide.MoveNext()) { d = deploy(user, host, url, (string)ide.Key, (string)ide.Value, (string)archRep.docs[ide.Key], downloadURL); if (d != null) archs.Add(ide.Key, d); } Subscriber sub = new Subscriber("active", archs); subscribers.Add(url, sub); Global.saveSubRep(); return url + " subscribed"; } } /// /// Adds an archive subscriber. /// /// status /// URL of the subscriber public string asubscribe(string url) { lock(this) { if (asubscribers.ContainsKey(url)) return url + " already subscribed"; Subscriber sub = new Subscriber("active", new Hashtable()); asubscribers.Add(url, sub); Global.saveSubRep(); return url + " subscribed"; } } /// /// Remove a subscriber. /// /// status /// user who issued the command /// host which issued the command /// URL of the subscriber /// If true force subscriber removal even if it cannot be contacted public string unsubscribe(string user, string host, string url, bool isForce) { lock(this) { if (!subscribers.ContainsKey(url)) return url + " is not subscribed"; Subscriber s = (Subscriber)subscribers[url]; int nbErr = 0; Hashtable archs = (Hashtable)s.archs.Clone(); IDictionaryEnumerator ide = archs.GetEnumerator(); while(ide.MoveNext()) { string val = (string)ide.Value; string arch = (string)ide.Key; if (val.Equals(arch + " pending deploy")) s.archs.Remove(ide.Key); else { if (undeploy(user, host, url, arch, (string)archRep.archives[arch], downloadURL)) s.archs.Remove(ide.Key); else { s.archs[ide.Key] = arch + " pending undeploy"; ++ nbErr; } } } if ((nbErr > 0) && !isForce) { s.state = "pending unsubscribe"; Global.saveSubRep(); return url + " pending unsubscribe"; } subscribers.Remove(url); Global.saveSubRep(); return url + " unsubscribed"; } } /// /// Remove an archive subscriber. /// /// status /// user who issued the command /// host which issued the command /// URL of the subscriber /// If true force subscriber removal even if it cannot be contacted public string unasubscribe(string user, string host, string url, bool isForce) { lock(this) { if (!asubscribers.ContainsKey(url)) return url + " is not subscribed"; Subscriber s = (Subscriber)asubscribers[url]; int nbErr = 0; Hashtable archs = (Hashtable)s.archs.Clone(); IDictionaryEnumerator ide = archs.GetEnumerator(); while(ide.MoveNext()) { string arch = (string)ide.Key; string val = (string)ide.Value; if (val.Equals(arch + " pending deploy")) s.archs.Remove(ide.Key); else { if (undeploy(user, host, url, arch, (string)archRep.archives[arch], downloadURL)) s.archs.Remove(ide.Key); else { s.archs[ide.Key] = arch + " pending undeploy"; ++ nbErr; } } } if ((nbErr > 0) && !isForce) { s.state = "pending unsubscribe"; Global.saveSubRep(); return url + " pending unsubscribe"; } asubscribers.Remove(url); Global.saveSubRep(); return url + " unsubscribed"; } } /// /// Adds an archive to an archive subscriber. /// /// user who issued the command /// host which issued the command /// subscriber URL /// archive public void add(string user, string host, string url, string arch) { lock(this) { if (!archRep.archives.Contains(arch)) return; string owner = (string)archRep.archives[arch]; string doc = (string)archRep.docs[arch]; string d = deploy(user, host, url, arch, owner, doc, downloadURL); if (d != null) { Subscriber sub = (Subscriber)asubscribers[url]; sub.archs.Add(arch, d); Global.saveSubRep(); } } } /// /// Removes an archive from an archive subscriber. /// /// user who issued the command /// host which issued the command /// subscriber /// archive public void delete(string user, string host, string url, string arch) { lock(this) { Subscriber sub = (Subscriber)asubscribers[url]; string d = (string)sub.archs[arch]; if (d.Equals(arch + " pending deploy")) sub.archs.Remove(arch); else { if (undeploy(user, host, url, arch, (string)archRep.archives[arch], downloadURL)) sub.archs.Remove(arch); else sub.archs[arch] = arch + " pending undeploy"; } Global.saveSubRep(); } } /// /// Retry deployments/undeployments /// public void cleanup() { lock(this) { bool rc = cleanup2(internalSubs); if (cleanup2(internalAsubs)) rc = true; if (rc) Global.saveSubRep(); } } /// /// Clean either subscribers or asubscribers. /// /// true if subscribers.xml must be saved /// subscribers or asubscribers private bool cleanup2(Hashtable ht) { Hashtable htc = (Hashtable)ht.Clone(); bool rc = false; IDictionaryEnumerator ide = htc.GetEnumerator(); while(ide.MoveNext()) { string url = (string)ide.Key; Subscriber s = (Subscriber)ide.Value; Hashtable h = (Hashtable)s.internalArchs.Clone(); IDictionaryEnumerator side = h.GetEnumerator(); while(side.MoveNext()) { string arch = (string)side.Key; string state = (string)side.Value; string owner = (string)archRep.archives[arch]; string doc = (string)archRep.docs[arch]; if (state.Equals(arch + " pending deploy")) { state = deploy("Cleanup", "Service", url, arch, owner, doc, downloadURL); if (state != null) { s.internalArchs[arch] = state; rc = true; } } else if (state.Equals(arch + " pending undeploy")) { if (undeploy("Cleanup", "Service", url, arch, owner, downloadURL)) { s.internalArchs.Remove(arch); rc = true; } } } if (s.state.Equals("pending unsubscribe")) { if (s.internalArchs.Count == 0) ht.Remove(url); } } return rc; } } }