using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; using System.Xml; using System.IO; using System.Web.Services.Protocols; using System.Runtime.Serialization.Formatters.Soap; namespace PageBox { /// ///

ActiveNaming Web Service. /// See Grid for explanations.

///

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.
///
[WebService(Namespace="PageBox")] public class ActiveNaming : System.Web.Services.WebService { /// /// Describes the Web services known on the PageBox that deployed them. /// Key: ArchiveService object /// Value: object describing the key range or whatever that can help to route requests /// Hashtable activeNames; /// /// Key: repository URL /// Value: Hashtable whose key is archive and value is Hashtable whose key is Web service /// and value CacheEntry. /// Hashtable cache = null; /// /// Serialization file name. Set in constructor. /// string filename; /// /// Name of the Cache Serialization file. Set in restoreCache. /// string cachename = null; /// /// Constructor. /// Calls InitializeComponent and restores activeNames /// public ActiveNaming() { filename = Global.path + "\\keyRanges.xml"; if (File.Exists(filename)) { SoapFormatter sf = new SoapFormatter(); FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); activeNames = (Hashtable)sf.Deserialize(fs); fs.Close(); } else activeNames = new Hashtable(); InitializeComponent(); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { } #endregion protected override void Dispose( bool disposing ) { } /// /// Method invoked by the client application to get candidate instances. /// /// Repository where the Web service has been published /// Name of the archive where the Web service was packaged /// Web service name /// An array of Candidate objects [WebMethod] public Candidate[] GetCandidate(string repository_URL, string WebService_archive, string WebService_name) { Candidate[] candidates = GetCandidateFromCache(repository_URL, WebService_archive, WebService_name, false); if (candidates != null) return candidates; RepoQuery rq = new RepoQuery(repository_URL); try { rq.Discover(); } catch(Exception e) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], WebService_archive, "ActiveNaming.GetCandidate(" + repository_URL + ", " + WebService_archive + ") returned " + repository_URL + " repository not found"); return GetCandidateFromCache(repository_URL, WebService_archive, WebService_name, true); } try { string[] subs = rq.GetSubscribers(WebService_archive, null); if (subs == null) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], WebService_archive, "ActiveNaming.GetCandidate(" + repository_URL + ", " + WebService_archive + ") found no subscriber"); return null; } else { candidates = new Candidate[subs.Length]; for (int i = 0; i < subs.Length; ++i) candidates[i] = GetCandidate2(subs[i], WebService_archive, WebService_name); SetCandidateInCache(repository_URL, WebService_archive, WebService_name, candidates); return candidates; } } catch(System.Net.WebException we) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], WebService_archive, "ActiveNaming.GetCandidate(" + repository_URL + ", " + WebService_archive + ") returned " + we.Message); return null; } catch(System.Web.HttpException he) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], WebService_archive, "ActiveNaming.GetCandidate(" + repository_URL + ", " + WebService_archive + ") returned " + he.Message); return null; } catch(SoapException se) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], WebService_archive, "ActiveNaming.GetCandidate(" + repository_URL + ", " + WebService_archive + ") returned " + se.Message); return null; } } /// /// Get candidates from cache. Called by GetCandidate. /// /// Repository where the Web service has been published /// Name of the archive where the Web service was packaged /// Web service name /// If false, returns candidates only if the entry is recent enough. /// If true, returns candidates if the entry exists. /// List of candidates private Candidate[] GetCandidateFromCache(string repository_URL, string WebService_archive, string WebService_name, bool force) { lock(this) { if (cache == null) restoreCache(); if (!cache.Contains(repository_URL)) return null; Hashtable archCache = (Hashtable)cache[repository_URL]; if (!archCache.Contains(WebService_archive)) return null; Hashtable WSCache = (Hashtable)archCache[WebService_archive]; if (!WSCache.Contains(WebService_name)) return null; CacheEntry ce = (CacheEntry)WSCache[WebService_name]; DateTime dt = DateTime.Now; if (!force && (dt.CompareTo(ce.expiration) > 0)) return null; return ce.candidates; } } /// /// Set a cache entry in the cache and persists the cache in activeCache.xml using /// XmlSerializer. Called by GetCandidate. /// /// Repository where the Web service has been published /// Name of the archive where the Web service was packaged /// Web service name /// An array of Candidate objects private void SetCandidateInCache(string repository_URL, string WebService_archive, string WebService_name, Candidate[] candidates) { lock(this) { if (cache == null) restoreCache(); if (!cache.Contains(repository_URL)) cache.Add(repository_URL, new Hashtable()); Hashtable archCache = (Hashtable)cache[repository_URL]; if (!archCache.Contains(WebService_archive)) archCache.Add(WebService_archive, new Hashtable()); Hashtable WSCache = (Hashtable)archCache[WebService_archive]; if (WSCache.Contains(WebService_name)) WSCache[WebService_name] = new CacheEntry(candidates); else WSCache.Add(WebService_name, new CacheEntry(candidates)); SoapFormatter sf = new SoapFormatter(); FileStream fs = new FileStream(cachename, FileMode.OpenOrCreate, FileAccess.Write); sf.Serialize(fs, cache); fs.Close(); } } /// /// Restores the cache from activeCache.xml into cache using XmlSerializer. /// Called by GetCandidateFromCache and SetCandidateInCache. /// private void restoreCache() { if (cachename == null) cachename = Global.path + "\\activeCache.xml"; if (File.Exists(cachename)) { SoapFormatter sf = new SoapFormatter(); FileStream fs = new FileStream(cachename, FileMode.Open, FileAccess.Read); cache = (Hashtable)sf.Deserialize(fs); fs.Close(); } else cache = new Hashtable(); } /// /// Query ActiveNaming.GetKeyRange of the subscriber. Called by GetCandidate. /// /// Subscriber /// Archive name /// Name of the Web service /// Candidate object private Candidate GetCandidate2(string sub, string archive, string service) { string url = "http://" + sub.Replace("Deploy.asmx", "ActiveNaming.asmx"); Candidate c = new Candidate(); ActiveProxy ap = new ActiveProxy(url); try { ap.Discover(); } catch(Exception e) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], archive, "ActiveNaming.GetCandidate2(" + sub + ", " + archive + ", " + service + ") returned " + url + " PageBox not found"); c.url = sub; c.keyRange = null; return c; } try { c.url = sub; c.keyRange = ap.GetKeyRange(archive, service); return c; } catch(System.Net.WebException we) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], archive, "ActiveNaming.GetCandidate2(" + sub + ", " + archive + ", " + service + ") returned " + we.Message); } catch(System.Web.HttpException he) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], archive, "ActiveNaming.GetCandidate2(" + sub + ", " + archive + ", " + service + ") returned " + he.Message); } catch(SoapException se) { Log.write(Context.Request.ServerVariables["REMOTE_HOST"], archive, "ActiveNaming.GetCandidate2(" + sub + ", " + archive + ", " + service + ") returned " + se.Message); } c.url = sub; c.keyRange = null; return c; } /// /// Method called by or on behalf of the Web service instance /// /// Name of the archive where the Web service was packaged /// Web service name /// /// Serialized object describing the key range or whatever that can help to route requests [WebMethod] public void SetKeyRange(string WebService_archive, string WebService_name, string keyRange) { lock(this) { ArchiveService asn = new ArchiveService(WebService_archive, WebService_name); if (activeNames.Contains(asn)) activeNames[asn] = keyRange; else activeNames.Add(asn, keyRange); SoapFormatter sf = new SoapFormatter(); FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write); sf.Serialize(fs, activeNames); fs.Close(); } } /// /// Method called by the client PageBox in GetCandidate method /// /// Name of the archive where the Web service was packaged /// Web service name /// Object describing the key range or whatever that can help to route requests [WebMethod] public string GetKeyRange(string WebService_archive, string WebService_name) { lock(this) { ArchiveService asn = new ArchiveService(WebService_archive, WebService_name); if (activeNames.Contains(asn)) { return (string)activeNames[asn]; } else return null; } } /// /// Method called by or on behalf of the Web service instance, /// for install in a first de-install step. /// /// Name of the archive where the Web service was packaged /// Web service name /// False if the Web service is no longer described [WebMethod] public bool RemoveKeyRange(string WebService_archive, string WebService_name) { lock(this) { ArchiveService asn = new ArchiveService(WebService_archive, WebService_name); if (!activeNames.Contains(asn)) return false; activeNames.Remove(asn); SoapFormatter sf = new SoapFormatter(); FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write); sf.Serialize(fs, activeNames); fs.Close(); return true; } } } /// /// Describes a cache entry. Stored in cache. /// [Serializable()] public class CacheEntry { /// /// Time when the cache entry becomes invalid /// public DateTime expiration; /// /// Candidate list /// public Candidate[] candidates; /// /// Constructor. Set the expiration time at now + 2h. /// /// Candidate list public CacheEntry(Candidate[] candidates) { this.candidates = candidates; expiration = DateTime.Now.AddHours(2); } } /// /// Describes a candidate instance /// [Serializable()] public class Candidate { /// /// URL of the PageBox that deployed the Web service /// public string url; /// /// Serialized object describing the key range or whatever that can help to route requests /// public string keyRange; } /// /// Key used to retrieve objects in activeNames /// [Serializable()] class ArchiveService { /// /// Name of the archive where the Web service was packaged. /// Public because persisted by XMLSerializer. /// public string WebService_archive; /// /// Web service name. /// Public because persisted by XMLSerializer. /// public string WebService_name; /// /// Hash code of the contenation of WebService_archive and WebService_name. /// private int hashCode = 0; /// /// Constructor. Populates ArchiveService. /// /// Name of the archive where the Web service was packaged /// Web service name public ArchiveService(string WebService_archive, string WebService_name) { this.WebService_archive = WebService_archive; this.WebService_name = WebService_name; } public ArchiveService() { } /// /// Overriden GetHashCode - Needed because ArchiveService is the key of activeNames /// /// hashcode of the concatenated strings public override int GetHashCode() { if (hashCode == 0) { string archiveName = WebService_archive + WebService_name; hashCode = archiveName.GetHashCode(); } return hashCode; } /// /// Overriden Equals - Needed because ArchiveService is the key of activeNames /// /// Other object /// Return true if ArchiveService and /// WebService_archive, WebService_name have the same value public override bool Equals(object as1) { if (!(as1 is ArchiveService)) return false; ArchiveService as2 = (ArchiveService)as1; if (as2.WebService_archive.Equals(WebService_archive) && as2.WebService_name.Equals(WebService_name)) return true; else return false; } } }