/SparkleLib/SparkleListenerBase.cs

http://github.com/hbons/SparkleShare · C# · 211 lines · 137 code · 57 blank · 17 comment · 18 complexity · 58cb6bf0fbd4306f2479c73b66647770 MD5 · raw file

  1. // SparkleShare, a collaboration and sharing tool.
  2. // Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as
  6. // published by the Free Software Foundation, either version 3 of the
  7. // License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Timers;
  19. namespace SparkleLib {
  20. public enum DisconnectReason {
  21. None,
  22. TimeOut,
  23. SystemSleep
  24. }
  25. // A persistent connection to the server that
  26. // listens for change notifications
  27. public abstract class SparkleListenerBase {
  28. public event Action Connected = delegate { };
  29. public event DisconnectedEventHandler Disconnected = delegate { };
  30. public delegate void DisconnectedEventHandler (DisconnectReason reason);
  31. public event AnnouncementReceivedEventHandler AnnouncementReceived = delegate { };
  32. public delegate void AnnouncementReceivedEventHandler (SparkleAnnouncement announcement);
  33. public readonly Uri Server;
  34. public abstract void Connect ();
  35. public abstract bool IsConnected { get; }
  36. public abstract bool IsConnecting { get; }
  37. protected abstract void AnnounceInternal (SparkleAnnouncement announcent);
  38. protected abstract void AlsoListenToInternal (string folder_identifier);
  39. protected List<string> channels = new List<string> ();
  40. private int max_recent_announcements = 10;
  41. private Dictionary<string, List<SparkleAnnouncement>> recent_announcements =
  42. new Dictionary<string, List<SparkleAnnouncement>> ();
  43. private Dictionary<string, SparkleAnnouncement> queue_up = new Dictionary<string, SparkleAnnouncement> ();
  44. private Timer reconnect_timer = new Timer {
  45. Interval = 60 * 1000,
  46. Enabled = true
  47. };
  48. public SparkleListenerBase (Uri server, string folder_identifier)
  49. {
  50. Server = server;
  51. this.channels.Add (folder_identifier);
  52. this.reconnect_timer.Elapsed += delegate {
  53. if (!IsConnected && !IsConnecting)
  54. Reconnect ();
  55. };
  56. this.reconnect_timer.Start ();
  57. }
  58. public void Announce (SparkleAnnouncement announcement)
  59. {
  60. if (!IsRecentAnnouncement (announcement)) {
  61. if (IsConnected) {
  62. SparkleLogger.LogInfo ("Listener", "Announcing message " + announcement.Message +
  63. " to " + announcement.FolderIdentifier + " on " + Server);
  64. AnnounceInternal (announcement);
  65. AddRecentAnnouncement (announcement);
  66. } else {
  67. SparkleLogger.LogInfo ("Listener", "Can't send message to " + Server + ". Queuing message");
  68. this.queue_up [announcement.FolderIdentifier] = announcement;
  69. }
  70. } else {
  71. SparkleLogger.LogInfo ("Listener", "Already processed message " + announcement.Message +
  72. " to " + announcement.FolderIdentifier + " from " + Server);
  73. }
  74. }
  75. public void AlsoListenTo (string channel)
  76. {
  77. if (!this.channels.Contains (channel))
  78. this.channels.Add (channel);
  79. if (IsConnected) {
  80. SparkleLogger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server);
  81. AlsoListenToInternal (channel);
  82. }
  83. }
  84. public void Reconnect ()
  85. {
  86. SparkleLogger.LogInfo ("Listener", "Trying to reconnect to " + Server);
  87. Connect ();
  88. }
  89. public void OnConnected ()
  90. {
  91. foreach (string channel in this.channels.GetRange (0, this.channels.Count)) {
  92. SparkleLogger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server);
  93. AlsoListenToInternal (channel);
  94. }
  95. SparkleLogger.LogInfo ("Listener", "Listening for announcements on " + Server);
  96. Connected ();
  97. if (this.queue_up.Count > 0) {
  98. SparkleLogger.LogInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages...");
  99. foreach (KeyValuePair<string, SparkleAnnouncement> item in this.queue_up) {
  100. SparkleAnnouncement announcement = item.Value;
  101. Announce (announcement);
  102. }
  103. }
  104. }
  105. public void OnDisconnected (DisconnectReason reason, string message)
  106. {
  107. SparkleLogger.LogInfo ("Listener", "Disconnected from " + Server + ": " + message);
  108. Disconnected (reason);
  109. }
  110. public void OnAnnouncement (SparkleAnnouncement announcement)
  111. {
  112. SparkleLogger.LogInfo ("Listener", "Got message " + announcement.Message + " from " +
  113. announcement.FolderIdentifier + " on " + Server);
  114. if (IsRecentAnnouncement (announcement))
  115. return;
  116. AddRecentAnnouncement (announcement);
  117. AnnouncementReceived (announcement);
  118. }
  119. public virtual void Dispose ()
  120. {
  121. if (this.reconnect_timer != null) {
  122. this.reconnect_timer.Stop ();
  123. this.reconnect_timer.Dispose ();
  124. }
  125. }
  126. private bool IsRecentAnnouncement (SparkleAnnouncement announcement)
  127. {
  128. if (!this.recent_announcements.ContainsKey (announcement.FolderIdentifier)) {
  129. return false;
  130. } else {
  131. foreach (SparkleAnnouncement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) {
  132. if (recent_announcement.Message.Equals (announcement.Message))
  133. return true;
  134. }
  135. return false;
  136. }
  137. }
  138. private List<SparkleAnnouncement> GetRecentAnnouncements (string folder_identifier)
  139. {
  140. if (!this.recent_announcements.ContainsKey (folder_identifier))
  141. this.recent_announcements [folder_identifier] = new List<SparkleAnnouncement> ();
  142. return this.recent_announcements [folder_identifier];
  143. }
  144. private void AddRecentAnnouncement (SparkleAnnouncement announcement)
  145. {
  146. List<SparkleAnnouncement> recent_announcements =
  147. GetRecentAnnouncements (announcement.FolderIdentifier);
  148. if (!IsRecentAnnouncement (announcement))
  149. recent_announcements.Add (announcement);
  150. if (recent_announcements.Count > this.max_recent_announcements)
  151. recent_announcements.RemoveRange (0, recent_announcements.Count - this.max_recent_announcements);
  152. }
  153. }
  154. }