Developer Area

root/apps/outlook/branches/1.5/DotTelSystem/Lookup/Cache/CacheUpdater.cs @ 588

Revision 588, 19.5 kB (checked in by jonmaycock, 10 months ago)
Line 
1///<summary>
2/// Author: Nick Brunwin
3///------------------------
4///Copyright (c) 2008, Telnic Ltd.
5///
6///All rights reserved.
7///
8///Redistribution and use in source and binary forms, with or
9///without modification, are permitted provided that the following
10///conditions are met:
11///
12///*    Redistributions of source code must retain the above
13///     copyright notice, this list of conditions and the
14///     following disclaimer.
15///*    Redistributions in binary form must reproduce the above
16///     copyright notice, this list of conditions and the following
17///     disclaimer in the documentation and/or other materials
18///     provided with the distribution.
19///*    Neither the name of the Telnic Ltd. nor the names of its
20///     contributors may be used to endorse or promote products
21///     derived from this software without specific prior written
22///     permission.
23///
24///THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25///CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26///INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27///MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28///DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29///CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30///SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31///LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32///USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33///AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34///LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
35///IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
36///THE POSSIBILITY OF SUCH DAMAGE.
37///--------------------------
38///</summary>
39using System;
40using System.Collections.Generic;
41using System.Text;
42using Outlook = Microsoft.Office.Interop.Outlook;
43using org.telnic.outlook.lookup;
44using System.Threading;
45using org.telnic.outlook.util;
46using org.telnic.outlook.friending;
47using System.Net;
48using org.telnic.outlook.prefs;
49
50namespace org.telnic.outlook.lookup.cache
51{
52    /// <summary>
53    /// Updates the caches for the telnames of the user's contacts.
54    /// </summary>
55    public class CacheUpdater : LookupTelnameCompleteListener
56    {
57        private Dictionary<Outlook.ContactItem, string> contactsTelnames;
58        private List<LookupTelname> lookups;
59        private int successfulUpdates = 0;
60        private int updates = 0;
61        private Thread cacheThread;
62
63        private bool cancelled = false;
64        /// <summary>
65        /// True if the update has been cancelled
66        /// </summary>
67        public bool Cancelled
68        {
69            get
70            {
71                return this.cancelled;
72            }
73        }
74
75        private bool done = false;
76        /// <summary>
77        /// True if the update is complete
78        /// </summary>
79        public bool Done
80        {
81            get
82            {
83                return this.done;
84            }
85        }
86
87        private Dictionary<string, bool> triedAndWhetherUpdated;
88        private Dictionary<string, List<Outlook.ContactItem>> beingLookedUp;
89        private Dictionary<string, string> previouslyUnsuccessful;
90
91        public CacheUpdater(Dictionary<Outlook.ContactItem, string> telnames)
92        {
93            Logger.Log(Logger.Section.Main_Toolbar, "Update all contacts called with " + telnames.Count + " telnames");
94            this.contactsTelnames = telnames;
95
96            //avoids looking up the same telname twice and provides a
97            //quick way to know if the telname should be marked as updated
98            this.triedAndWhetherUpdated = new Dictionary<string, bool>();
99            this.beingLookedUp = new Dictionary<string, List<Outlook.ContactItem>>();
100            this.previouslyUnsuccessful = new Dictionary<string, string>();
101        }
102
103        /// <summary>
104        /// Mark the given telname's changes as seen
105        /// </summary>
106        /// <param name="telname">The telname to mark as seen</param>
107        public void SetAsSeen(string telname)
108        {
109            LookupCache.SetAsSeen(telname);
110        }
111
112        /// <summary>
113        /// Perform a lookup for each telname in a list and cache the results
114        /// Optimized for machines that take a while to get back DNS responses.
115        /// Typically only one lookup happens at once.
116        /// </summary>
117        private void UpdateCaches()
118        {
119            cancelled = false;
120            this.lookups = new List<LookupTelname>();
121            int count = 0;
122            foreach (KeyValuePair<Outlook.ContactItem, string> kvp in contactsTelnames)
123            {
124                count++;
125                string telname = kvp.Value;
126                Logger.Log(Logger.Section.Lookups, telname + " is number " + count);
127                Outlook.ContactItem contact = kvp.Key;
128                try
129                {
130                    string privateTelname = Publishers.Instance.GetPrivateTelname(telname);
131
132                   
133
134                    lock (this.beingLookedUp)
135                    {
136                        if (privateTelname == null && triedAndWhetherUpdated.ContainsKey(telname))
137                        {
138                            Logger.Log(Logger.Section.Lookups, "Already know the answer for " + telname);
139                            this.TelnameAlreadySeen(telname, contact, telname);
140                        }
141                        else if (privateTelname != null && triedAndWhetherUpdated.ContainsKey(privateTelname))
142                        {
143                            Logger.Log(Logger.Section.Lookups, "Already know the answer for " + privateTelname);
144                            this.TelnameAlreadySeen(telname, contact, privateTelname);
145                        }
146                        else if (privateTelname == null && this.beingLookedUp.ContainsKey(telname))
147                        {
148                            //no point in looking it up twice
149                            Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + telname);
150                            this.beingLookedUp[telname].Add(contact);
151                        }
152                        else if (privateTelname != null && this.beingLookedUp.ContainsKey(privateTelname))
153                        {
154                            Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + privateTelname);
155                            this.beingLookedUp[privateTelname].Add(contact);
156                        }
157                        else //requires looking up
158                        {
159                            if (privateTelname != null && this.previouslyUnsuccessful.ContainsKey(privateTelname))
160                            {
161                                Logger.Log(Logger.Section.Lookups, privateTelname + " was previously unsuccessful.");
162                                privateTelname = null;
163                            }
164                            if (privateTelname == null && this.previouslyUnsuccessful.ContainsKey(telname))
165                            {
166                                Logger.Log(Logger.Section.Lookups, telname + " was previously unsuccessful.");
167                                this.IncrementUpdateAndConsider();
168                            }
169                            else
170                            {
171                                List<Outlook.ContactItem> contacts = new List<Outlook.ContactItem>();
172                                contacts.Add(contact);
173                                if (privateTelname == null)
174                                {
175                                    privateTelname = telname;
176                                }
177                                this.beingLookedUp.Add(privateTelname, contacts);
178
179                                Logger.Log(Logger.Section.Main_Toolbar, "Updating cache for " + privateTelname);
180                                LookupTelname lt = new LookupTelname(IPAddress.Parse(Preferences.GetPreference("dns")),
181                                                                                int.Parse(Preferences.GetPreference("dnsport")));
182                                lt.Listener = this;
183                                this.lookups.Add(lt);
184
185                                lt.performLookup(privateTelname, telname);
186                            }
187                        }
188                    }
189                }
190                catch (Exception e)
191                {
192                    Logger.Log(Logger.Section.Main_Toolbar, "Update all contacts exception, moving onto next contact: "
193                                                                                                            + e.Message);
194                    this.IncrementUpdateAndConsider();
195                }
196            }
197
198        }
199
200        private void IncrementUpdateAndConsider()
201        {
202            this.updates++;
203            this.UpdateProgressBar();
204            this.ConsiderStopping();
205        }
206
207        /// <summary>
208        /// Handles the case when a telname has already been seen
209        /// </summary>
210        /// <param name="telname">The telname as it is displayed to the user</param>
211        /// <param name="contact">The contact that has the given telname</param>
212        /// <param name="actualTelname">The actual telname that has already been seen</param>
213        private void TelnameAlreadySeen(string telname, Outlook.ContactItem contact, string actualTelname)
214        {
215            if (this.triedAndWhetherUpdated[actualTelname])
216            {
217                //update table
218                this.AddUpdatedContact(contact, telname);
219            }
220            //update progress
221            this.successfulUpdates++;
222            this.IncrementUpdateAndConsider();
223        }
224
225        /// <summary>
226        /// For passing updates of the progress of the update
227        /// </summary>
228        public class ProgressChangedEventArgs : EventArgs
229        {
230            private double progress;
231            public double Progress
232            {
233                get
234                {
235                    return this.progress;
236                }
237            }
238
239            public ProgressChangedEventArgs(double progress)
240            {
241                this.progress = progress;
242            }
243        }
244
245        public delegate void ProgressChangedHandler(object sender, ProgressChangedEventArgs e);
246
247        /// <summary>
248        /// Fires whenever the progress of the update changes
249        /// </summary>
250        public event ProgressChangedHandler ProgressChanged;
251
252        /// <summary>
253        /// Fires the ProgressChanged event
254        /// </summary>
255        private void UpdateProgressBar()
256        {
257            if (this.ProgressChanged != null)
258            {
259                this.ProgressChanged(this, 
260                        new ProgressChangedEventArgs(((double)this.updates) / ((double)this.contactsTelnames.Count)));
261            }
262        }
263
264        /// <summary>
265        /// For notifying that a contact with a given telname has been updated.
266        /// </summary>
267        public class UpdatedContactFoundEventArgs : EventArgs
268        {
269            private Outlook.ContactItem contact;
270            public Outlook.ContactItem Contact
271            {
272                get
273                {
274                    return this.contact;
275                }
276            }
277
278            private string telname;
279            public string Telname
280            {
281                get
282                {
283                    return this.telname;
284                }
285            }
286
287            public UpdatedContactFoundEventArgs(Outlook.ContactItem contact, string telname)
288            {
289                this.contact = contact;
290                this.telname = telname;
291            }
292        }
293
294        public delegate void UpdatedContactFoundHandler(object sender, UpdatedContactFoundEventArgs e);
295
296        /// <summary>
297        /// Fires everytime a changed telname is noticed
298        /// </summary>
299        public event UpdatedContactFoundHandler UpdatedContactFound;
300
301        private void AddUpdatedContact(Outlook.ContactItem contact, string telname)
302        {
303            if (this.UpdatedContactFound != null)
304            {
305                this.UpdatedContactFound(this, new UpdatedContactFoundEventArgs(contact, telname));
306            }
307        }
308
309        /// <summary>
310        /// Process the result of the lookup
311        /// </summary>
312        /// <param name="lr">The result to process</param>
313        public void LookupTelnameComplete(LookupResult lr)
314        {
315            if (cancelled)
316            {
317                return;
318            }
319            try
320            {
321                lock (this.beingLookedUp)
322                {
323                    if (lr.NaptrStatus == LookupWorker.LookupStatus.Nxdomain && lr.Telname != lr.PublicTelname)
324                    {
325                        if (!this.previouslyUnsuccessful.ContainsKey(lr.Telname))
326                        {
327                            this.previouslyUnsuccessful.Add(lr.Telname, lr.Telname);
328                        }
329                        //lookup domain above instead
330                        string parentDomain = lr.PublicTelname;
331
332                        //need to spawn multiple look ups
333                        foreach (Outlook.ContactItem contact in this.beingLookedUp[lr.Telname])
334                        {
335                            string tname = this.contactsTelnames[contact];
336                            if (this.triedAndWhetherUpdated.ContainsKey(tname))
337                            {
338                                Logger.Log(Logger.Section.Lookups, "Already know the answer for " + tname + "!");
339                                this.TelnameAlreadySeen(tname, contact, tname);
340                            }
341                            else if (this.beingLookedUp.ContainsKey(tname))
342                            {
343                                Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + tname + "!");
344                                this.beingLookedUp[tname].Add(contact);
345                            }
346                            else
347                            {
348                                Logger.Log(Logger.Section.Lookups, "Starting new lookup for " + tname + "!");
349                                List<Outlook.ContactItem> cons = new List<Outlook.ContactItem>();
350                                cons.Add(contact);
351                                this.beingLookedUp.Add(tname, cons);
352                                LookupTelname lt = new LookupTelname(IPAddress.Parse(Preferences.GetPreference("dns")),
353                                                                        int.Parse(Preferences.GetPreference("dnsport")));
354                                lt.Listener = this;
355                                lt.performLookup(tname, tname);
356                                lookups.Add(lt);
357                            }
358                        }
359
360                        this.beingLookedUp.Remove(lr.Telname);
361                        return;
362                    }
363
364                    //remove from being looked up asap
365                    List<Outlook.ContactItem> contacts = this.beingLookedUp[lr.Telname];
366                    this.beingLookedUp.Remove(lr.Telname);
367
368                    if (lr.Changed)
369                    {
370                        foreach (Outlook.ContactItem contact in contacts)
371                        {
372                            this.AddUpdatedContact(contact, lr.PublicTelname);
373                        }
374                    }
375
376                    if (!this.triedAndWhetherUpdated.ContainsKey(lr.Telname))
377                    {
378                        this.triedAndWhetherUpdated.Add(lr.Telname, lr.Changed);
379                    }
380
381                    if (lr.HasResults())
382                    {
383                        successfulUpdates += contacts.Count;
384                    }
385                    else if (!this.previouslyUnsuccessful.ContainsKey(lr.Telname))
386                    {
387                        this.previouslyUnsuccessful.Add(lr.Telname, lr.Telname);
388                    }
389                    this.updates += contacts.Count;
390
391
392                    Logger.Log(Logger.Section.Main_Toolbar, "Lookup complete for " + lr.Telname + ", "
393                                                            + (contactsTelnames.Count - updates) + " more results to cache");
394
395                    this.UpdateProgressBar();
396                    this.ConsiderStopping();
397                }
398            }
399            catch (Exception e)
400            {
401                Logger.Log(Logger.Section.Main_Toolbar, e.Message);
402            }
403        }
404
405        /// <summary>
406        /// If all the contacts are processed stop
407        /// </summary>
408        private void ConsiderStopping()
409        {
410            if (contactsTelnames.Count == this.updates)
411            {
412                this.Stop();
413            }
414        }
415
416        public delegate void StartHandler(object sender, EventArgs e);
417
418        /// <summary>
419        /// Fired when the update checking process starts
420        /// </summary>
421        public event StartHandler Started;
422
423        private void Start()
424        {
425            if (this.Started != null)
426            {
427                this.Started(this, EventArgs.Empty);
428            }
429        }
430
431        public delegate void StopHandler(object sender, EventArgs e);
432        /// <summary>
433        /// Fired when the update checking process ends
434        /// </summary>
435        public event StopHandler Stopped;
436
437        private void Stop()
438        {
439            Logger.Log(Logger.Section.Main_Toolbar, "Operation Completed. Cached " + successfulUpdates + " unique domains, " + this.updates + " attempted");
440            if (lookups != null)
441            {
442                lookups.Clear();
443            }
444
445            if (this.Stopped != null)
446            {
447                this.Stopped(this, EventArgs.Empty);
448            }
449
450            this.done = true;
451        }
452
453        /// <summary>
454        /// Cancel the update checking process.
455        /// </summary>
456        public void Cancel()
457        {
458            Logger.Log(Logger.Section.Main_Toolbar, "Operation Cancelled");
459            cancelled = true;
460            Logger.Log(Logger.Section.Lookups, "" + this.beingLookedUp.Count + " still to finish");
461            foreach(KeyValuePair<string, List<Outlook.ContactItem>> kvp in this.beingLookedUp)
462            {
463                Logger.Log(Logger.Section.Lookups, "Telname: " + kvp.Key + ": " + kvp.Value.Count);
464            }
465            if (lookups != null)
466            {
467                foreach (LookupTelname lt in lookups.ToArray())
468                {
469                    lt.cancel();
470                    lookups.Remove(lt);
471                }
472            }
473
474            try
475            {
476                cacheThread.Abort();
477            }
478            catch (Exception)
479            {
480                cacheThread = null;
481            }
482
483            Stop();
484        }
485
486        /// <summary>
487        /// Starts the process off
488        /// </summary>
489        public void Ready()
490        {
491            Start();
492            if (this.contactsTelnames.Count == 0)
493            {
494                Stop();
495            }
496            else
497            {
498                cacheThread = new Thread(UpdateCaches);
499                cacheThread.Name = "Update all contacts thread";
500                cacheThread.Start();
501            }
502        }
503    }
504}
Note: See TracBrowser for help on using the browser.
Telnic
Search This Site
Partners
Neustar
ICANN
Main site | WHOIS | Sell .tel | FAQ | Archived Site | About Telnic | Contact Us