/SparkleLib/SparkleListenerBase.cs
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 17 18using System; 19using System.Collections.Generic; 20using System.Timers; 21 22namespace SparkleLib { 23 24 public enum DisconnectReason { 25 None, 26 TimeOut, 27 SystemSleep 28 } 29 30 31 // A persistent connection to the server that 32 // listens for change notifications 33 public abstract class SparkleListenerBase { 34 35 public event Action Connected = delegate { }; 36 37 public event DisconnectedEventHandler Disconnected = delegate { }; 38 public delegate void DisconnectedEventHandler (DisconnectReason reason); 39 40 public event AnnouncementReceivedEventHandler AnnouncementReceived = delegate { }; 41 public delegate void AnnouncementReceivedEventHandler (SparkleAnnouncement announcement); 42 43 public readonly Uri Server; 44 45 public abstract void Connect (); 46 public abstract bool IsConnected { get; } 47 public abstract bool IsConnecting { get; } 48 49 50 protected abstract void AnnounceInternal (SparkleAnnouncement announcent); 51 protected abstract void AlsoListenToInternal (string folder_identifier); 52 53 protected List<string> channels = new List<string> (); 54 55 56 private int max_recent_announcements = 10; 57 58 private Dictionary<string, List<SparkleAnnouncement>> recent_announcements = 59 new Dictionary<string, List<SparkleAnnouncement>> (); 60 61 private Dictionary<string, SparkleAnnouncement> queue_up = new Dictionary<string, SparkleAnnouncement> (); 62 63 private Timer reconnect_timer = new Timer { 64 Interval = 60 * 1000, 65 Enabled = true 66 }; 67 68 69 public SparkleListenerBase (Uri server, string folder_identifier) 70 { 71 Server = server; 72 this.channels.Add (folder_identifier); 73 74 this.reconnect_timer.Elapsed += delegate { 75 if (!IsConnected && !IsConnecting) 76 Reconnect (); 77 }; 78 79 this.reconnect_timer.Start (); 80 } 81 82 83 public void Announce (SparkleAnnouncement announcement) 84 { 85 if (!IsRecentAnnouncement (announcement)) { 86 if (IsConnected) { 87 SparkleLogger.LogInfo ("Listener", "Announcing message " + announcement.Message + 88 " to " + announcement.FolderIdentifier + " on " + Server); 89 90 AnnounceInternal (announcement); 91 AddRecentAnnouncement (announcement); 92 93 } else { 94 SparkleLogger.LogInfo ("Listener", "Can't send message to " + Server + ". Queuing message"); 95 this.queue_up [announcement.FolderIdentifier] = announcement; 96 } 97 98 } else { 99 SparkleLogger.LogInfo ("Listener", "Already processed message " + announcement.Message + 100 " to " + announcement.FolderIdentifier + " from " + Server); 101 } 102 } 103 104 105 public void AlsoListenTo (string channel) 106 { 107 if (!this.channels.Contains (channel)) 108 this.channels.Add (channel); 109 110 if (IsConnected) { 111 SparkleLogger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server); 112 AlsoListenToInternal (channel); 113 } 114 } 115 116 117 public void Reconnect () 118 { 119 SparkleLogger.LogInfo ("Listener", "Trying to reconnect to " + Server); 120 Connect (); 121 } 122 123 124 public void OnConnected () 125 { 126 foreach (string channel in this.channels.GetRange (0, this.channels.Count)) { 127 SparkleLogger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server); 128 AlsoListenToInternal (channel); 129 } 130 131 SparkleLogger.LogInfo ("Listener", "Listening for announcements on " + Server); 132 Connected (); 133 134 if (this.queue_up.Count > 0) { 135 SparkleLogger.LogInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages..."); 136 137 foreach (KeyValuePair<string, SparkleAnnouncement> item in this.queue_up) { 138 SparkleAnnouncement announcement = item.Value; 139 Announce (announcement); 140 } 141 } 142 } 143 144 145 public void OnDisconnected (DisconnectReason reason, string message) 146 { 147 SparkleLogger.LogInfo ("Listener", "Disconnected from " + Server + ": " + message); 148 Disconnected (reason); 149 } 150 151 152 public void OnAnnouncement (SparkleAnnouncement announcement) 153 { 154 SparkleLogger.LogInfo ("Listener", "Got message " + announcement.Message + " from " + 155 announcement.FolderIdentifier + " on " + Server); 156 157 if (IsRecentAnnouncement (announcement)) 158 return; 159 160 AddRecentAnnouncement (announcement); 161 AnnouncementReceived (announcement); 162 } 163 164 165 public virtual void Dispose () 166 { 167 if (this.reconnect_timer != null) { 168 this.reconnect_timer.Stop (); 169 this.reconnect_timer.Dispose (); 170 } 171 } 172 173 174 private bool IsRecentAnnouncement (SparkleAnnouncement announcement) 175 { 176 if (!this.recent_announcements.ContainsKey (announcement.FolderIdentifier)) { 177 return false; 178 179 } else { 180 foreach (SparkleAnnouncement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) { 181 if (recent_announcement.Message.Equals (announcement.Message)) 182 return true; 183 } 184 185 return false; 186 } 187 } 188 189 190 private List<SparkleAnnouncement> GetRecentAnnouncements (string folder_identifier) 191 { 192 if (!this.recent_announcements.ContainsKey (folder_identifier)) 193 this.recent_announcements [folder_identifier] = new List<SparkleAnnouncement> (); 194 195 return this.recent_announcements [folder_identifier]; 196 } 197 198 199 private void AddRecentAnnouncement (SparkleAnnouncement announcement) 200 { 201 List<SparkleAnnouncement> recent_announcements = 202 GetRecentAnnouncements (announcement.FolderIdentifier); 203 204 if (!IsRecentAnnouncement (announcement)) 205 recent_announcements.Add (announcement); 206 207 if (recent_announcements.Count > this.max_recent_announcements) 208 recent_announcements.RemoveRange (0, recent_announcements.Count - this.max_recent_announcements); 209 } 210 } 211}