PageRenderTime 14ms CodeModel.GetById 1ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}