Developer Area

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

Revision 577, 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                   
131                    string privateTelname = Publishers.Instance.GetPrivateTelname(telname);
132
133                   
134
135                    lock (this.beingLookedUp)
136                    {
137                        if (privateTelname == null && triedAndWhetherUpdated.ContainsKey(telname))
138                        {
139                            Logger.Log(Logger.Section.Lookups, "Already know the answer for " + telname);
140                            this.TelnameAlreadySeen(telname, contact, telname);
141                        }
142                        else if (privateTelname != null && triedAndWhetherUpdated.ContainsKey(privateTelname))
143                        {
144                            Logger.Log(Logger.Section.Lookups, "Already know the answer for " + privateTelname);
145                            this.TelnameAlreadySeen(telname, contact, privateTelname);
146                        }
147                        else if (privateTelname == null && this.beingLookedUp.ContainsKey(telname))
148                        {
149                            //no point in looking it up twice
150                            Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + telname);
151                            this.beingLookedUp[telname].Add(contact);
152                        }
153                        else if (privateTelname != null && this.beingLookedUp.ContainsKey(privateTelname))
154                        {
155                            Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + privateTelname);
156                            this.beingLookedUp[privateTelname].Add(contact);
157                        }
158                        else //requires looking up
159                        {
160                            if (privateTelname != null && this.previouslyUnsuccessful.ContainsKey(privateTelname))
161                            {
162                                Logger.Log(Logger.Section.Lookups, privateTelname + " was previously unsuccessful.");
163                                privateTelname = null;
164                            }
165                            if (privateTelname == null && this.previouslyUnsuccessful.ContainsKey(telname))
166                            {
167                                Logger.Log(Logger.Section.Lookups, telname + " was previously unsuccessful.");
168                                this.IncrementUpdateAndConsider();
169                            }
170                            else
171                            {
172                                List<Outlook.ContactItem> contacts = new List<Outlook.ContactItem>();
173                                contacts.Add(contact);
174                                if (privateTelname == null)
175                                {
176                                    privateTelname = telname;
177                                }
178                                this.beingLookedUp.Add(privateTelname, contacts);
179
180                                Logger.Log(Logger.Section.Main_Toolbar, "Updating cache for " + privateTelname);
181                                LookupTelname lt = new LookupTelname(IPAddress.Parse(Preferences.GetPreference("dns")),
182                                                                                int.Parse(Preferences.GetPreference("dnsport")));
183                                lt.Listener = this;
184                                this.lookups.Add(lt);
185
186                                lt.performLookup(privateTelname, telname);
187                            }
188                        }
189                    }
190                }
191                catch (Exception e)
192                {
193                    Logger.Log(Logger.Section.Main_Toolbar, "Update all contacts exception, moving onto next contact: "
194                                                                                                            + e.Message);
195                    this.IncrementUpdateAndConsider();
196                }
197            }
198
199        }
200
201        private void IncrementUpdateAndConsider()
202        {
203            this.updates++;
204            this.UpdateProgressBar();
205            this.ConsiderStopping();
206        }
207
208        /// <summary>
209        /// Handles the case when a telname has already been seen
210        /// </summary>
211        /// <param name="telname">The telname as it is displayed to the user</param>
212        /// <param name="contact">The contact that has the given telname</param>
213        /// <param name="actualTelname">The actual telname that has already been seen</param>
214        private void TelnameAlreadySeen(string telname, Outlook.ContactItem contact, string actualTelname)
215        {
216            if (this.triedAndWhetherUpdated[actualTelname])
217            {
218                //update table
219                this.AddUpdatedContact(contact, telname);
220            }
221            //update progress
222            this.successfulUpdates++;
223            this.IncrementUpdateAndConsider();
224        }
225
226        /// <summary>
227        /// For passing updates of the progress of the update
228        /// </summary>
229        public class ProgressChangedEventArgs : EventArgs
230        {
231            private double progress;
232            public double Progress
233            {
234                get
235                {
236                    return this.progress;
237                }
238            }
239
240            public ProgressChangedEventArgs(double progress)
241            {
242                this.progress = progress;
243            }
244        }
245
246        public delegate void ProgressChangedHandler(object sender, ProgressChangedEventArgs e);
247
248        /// <summary>
249        /// Fires whenever the progress of the update changes
250        /// </summary>
251        public event ProgressChangedHandler ProgressChanged;
252
253        /// <summary>
254        /// Fires the ProgressChanged event
255        /// </summary>
256        private void UpdateProgressBar()
257        {
258            if (this.ProgressChanged != null)
259            {
260                this.ProgressChanged(this, 
261                        new ProgressChangedEventArgs(((double)this.updates) / ((double)this.contactsTelnames.Count)));
262            }
263        }
264
265        /// <summary>
266        /// For notifying that a contact with a given telname has been updated.
267        /// </summary>
268        public class UpdatedContactFoundEventArgs : EventArgs
269        {
270            private Outlook.ContactItem contact;
271            public Outlook.ContactItem Contact
272            {
273                get
274                {
275                    return this.contact;
276                }
277            }
278
279            private string telname;
280            public string Telname
281            {
282                get
283                {
284                    return this.telname;
285                }
286            }
287
288            public UpdatedContactFoundEventArgs(Outlook.ContactItem contact, string telname)
289            {
290                this.contact = contact;
291                this.telname = telname;
292            }
293        }
294
295        public delegate void UpdatedContactFoundHandler(object sender, UpdatedContactFoundEventArgs e);
296
297        /// <summary>
298        /// Fires everytime a changed telname is noticed
299        /// </summary>
300        public event UpdatedContactFoundHandler UpdatedContactFound;
301
302        private void AddUpdatedContact(Outlook.ContactItem contact, string telname)
303        {
304            if (this.UpdatedContactFound != null)
305            {
306                this.UpdatedContactFound(this, new UpdatedContactFoundEventArgs(contact, telname));
307            }
308        }
309
310        /// <summary>
311        /// Process the result of the lookup
312        /// </summary>
313        /// <param name="lr">The result to process</param>
314        public void LookupTelnameComplete(LookupResult lr)
315        {
316            if (cancelled)
317            {
318                return;
319            }
320            try
321            {
322                lock (this.beingLookedUp)
323                {
324                    if (lr.NaptrStatus == LookupWorker.LookupStatus.Nxdomain && lr.Telname != lr.PublicTelname)
325                    {
326                        if (!this.previouslyUnsuccessful.ContainsKey(lr.Telname))
327                        {
328                            this.previouslyUnsuccessful.Add(lr.Telname, lr.Telname);
329                        }
330                        //lookup domain above instead
331                        string parentDomain = lr.PublicTelname;
332
333                        //need to spawn multiple look ups
334                        foreach (Outlook.ContactItem contact in this.beingLookedUp[lr.Telname])
335                        {
336                            string tname = this.contactsTelnames[contact];
337                            if (this.triedAndWhetherUpdated.ContainsKey(tname))
338                            {
339                                Logger.Log(Logger.Section.Lookups, "Already know the answer for " + tname + "!");
340                                this.TelnameAlreadySeen(tname, contact, tname);
341                            }
342                            else if (this.beingLookedUp.ContainsKey(tname))
343                            {
344                                Logger.Log(Logger.Section.Lookups, "Adding another lookup for " + tname + "!");
345                                this.beingLookedUp[tname].Add(contact);
346                            }
347                            else
348                            {
349                                Logger.Log(Logger.Section.Lookups, "Starting new lookup for " + tname + "!");
350                                List<Outlook.ContactItem> cons = new List<Outlook.ContactItem>();
351                                cons.Add(contact);
352                                this.beingLookedUp.Add(tname, cons);
353                                LookupTelname lt = new LookupTelname(IPAddress.Parse(Preferences.GetPreference("dns")),
354                                                                        int.Parse(Preferences.GetPreference("dnsport")));
355                                lt.Listener = this;
356                                lt.performLookup(tname, tname);
357                                lookups.Add(lt);
358                            }
359                        }
360
361                        this.beingLookedUp.Remove(lr.Telname);
362                        return;
363                    }
364
365                    //remove from being looked up asap
366                    List<Outlook.ContactItem> contacts = this.beingLookedUp[lr.Telname];
367                    this.beingLookedUp.Remove(lr.Telname);
368
369                    if (lr.Changed)
370                    {
371                        foreach (Outlook.ContactItem contact in contacts)
372                        {
373                            this.AddUpdatedContact(contact, lr.PublicTelname);
374                        }
375                    }
376
377                    if (!this.triedAndWhetherUpdated.ContainsKey(lr.Telname))
378                    {
379                        this.triedAndWhetherUpdated.Add(lr.Telname, lr.Changed);
380                    }
381
382                    if (lr.HasResults())
383                    {
384                        successfulUpdates += contacts.Count;
385                    }
386                    else if (!this.previouslyUnsuccessful.ContainsKey(lr.Telname))
387                    {
388                        this.previouslyUnsuccessful.Add(lr.Telname, lr.Telname);
389                    }
390                    this.updates += contacts.Count;
391
392
393                    Logger.Log(Logger.Section.Main_Toolbar, "Lookup complete for " + lr.Telname + ", "
394                                                            + (contactsTelnames.Count - updates) + " more results to cache");
395
396                    this.UpdateProgressBar();
397                    this.ConsiderStopping();
398                }
399            }
400            catch (Exception e)
401            {
402                Logger.Log(Logger.Section.Main_Toolbar, e.Message);
403            }
404        }
405
406        /// <summary>
407        /// If all the contacts are processed stop
408        /// </summary>
409        private void ConsiderStopping()
410        {
411            if (contactsTelnames.Count == this.updates)
412            {
413                this.Stop();
414            }
415        }
416
417        public delegate void StartHandler(object sender, EventArgs e);
418
419        /// <summary>
420        /// Fired when the update checking process starts
421        /// </summary>
422        public event StartHandler Started;
423
424        private void Start()
425        {
426            if (this.Started != null)
427            {
428                this.Started(this, EventArgs.Empty);
429            }
430        }
431
432        public delegate void StopHandler(object sender, EventArgs e);
433        /// <summary>
434        /// Fired when the update checking process ends
435        /// </summary>
436        public event StopHandler Stopped;
437
438        private void Stop()
439        {
440            Logger.Log(Logger.Section.Main_Toolbar, "Operation Completed. Cached " + successfulUpdates + " unique domains, " + this.updates + " attempted");
441            if (lookups != null)
442            {
443                lookups.Clear();
444            }
445
446            if (this.Stopped != null)
447            {
448                this.Stopped(this, EventArgs.Empty);
449            }
450
451            this.done = true;
452        }
453
454        /// <summary>
455        /// Cancel the update checking process.
456        /// </summary>
457        public void Cancel()
458        {
459            Logger.Log(Logger.Section.Main_Toolbar, "Operation Cancelled");
460            cancelled = true;
461            Logger.Log(Logger.Section.Lookups, "" + this.beingLookedUp.Count + " still to finish");
462            foreach(KeyValuePair<string, List<Outlook.ContactItem>> kvp in this.beingLookedUp)
463            {
464                Logger.Log(Logger.Section.Lookups, "Telname: " + kvp.Key + ": " + kvp.Value.Count);
465            }
466            if (lookups != null)
467            {
468                foreach (LookupTelname lt in lookups.ToArray())
469                {
470                    lt.cancel();
471                    lookups.Remove(lt);
472                }
473            }
474
475            try
476            {
477                cacheThread.Abort();
478            }
479            catch (Exception)
480            {
481                cacheThread = null;
482            }
483
484            Stop();
485        }
486
487        /// <summary>
488        /// Starts the process off
489        /// </summary>
490        public void Ready()
491        {
492            Start();
493            if (this.contactsTelnames.Count == 0)
494            {
495                Stop();
496            }
497            else
498            {
499                cacheThread = new Thread(UpdateCaches);
500                cacheThread.Name = "Update all contacts thread";
501                cacheThread.Start();
502            }
503        }
504    }
505}
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