Indexable is not marked _done_ until all the child indexables (including child of...
[beagle.git] / beagled / WebServices / NetBeagleQueryable.cs
blobf6fd0c870dcada7f0add0c4a97cec913477ef3f6
1 //
2 // NetBeagleQueryable.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
6 // Authors:
7 // Vijay K. Nanjundaswamy (knvijay@novell.com)
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining a
12 // copy of this software and associated documentation files (the "Software"),
13 // to deal in the Software without restriction, including without limitation
14 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 // and/or sell copies of the Software, and to permit persons to whom the
16 // Software is furnished to do so, subject to the following conditions:
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 // DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.IO;
32 using System.Timers;
33 using System.Threading;
34 using System.Collections;
36 using Beagle;
37 using Beagle.Util;
39 namespace Beagle.Daemon {
41 [QueryableFlavor (Name="NetworkedBeagle", Domain=QueryDomain.Global, RequireInotify=false)]
42 public class NetworkedBeagle : IQueryable
44 static Logger log = Logger.Get ("NetworkedBeagle");
45 static readonly string NetBeagleConfigFile = "netbeagle.cfg";
46 public static readonly string BeagleNetPrefix = "netbeagle://";
47 public static bool NetBeagleListActive = false;
49 ArrayList NetBeagleList;
51 public NetworkedBeagle ()
53 NetBeagleList = new ArrayList ();
54 NetBeagleListActive = false;
57 /////////////////////////////////////////////////////////////////////////////////////////
58 private static Hashtable requestTable = Hashtable.Synchronized(new Hashtable());
59 private static Hashtable timerTable = Hashtable.Synchronized(new Hashtable());
61 private static int TimerInterval = 15000; //15 sec timer
62 private static int RequestCacheTime = 20; //Cache requests for 5 minutes
64 private static System.Timers.Timer cTimer = null;
66 static NetworkedBeagle () {
67 cTimer = new System.Timers.Timer(TimerInterval);
68 cTimer.Elapsed += new ElapsedEventHandler(TimerEventHandler);
69 cTimer.AutoReset = true;
70 cTimer.Enabled = false;
73 ~NetworkedBeagle()
75 cTimer.Close();
77 /////////////////////////////////////////////////////////////////////////////////////////
78 public string Name {
79 get { return "NetworkedBeagle"; }
82 public void Start ()
84 SetupNetBeagleList ();
85 Conf.Subscribe (typeof (Conf.NetworkingConfig), new Conf.ConfigUpdateHandler (NetBeagleConfigurationChanged));
88 void SetupNetBeagleList ()
90 //First check for ~/.beagle/config/networking.xml configuration
91 ArrayList NetBeagleNodes = Conf.Networking.NetBeagleNodes;
93 if ((NetBeagleNodes != null) && (NetBeagleNodes.Count > 0)) {
95 foreach (string nb in NetBeagleNodes) {
96 if (nb == null || nb == "")
97 continue;
98 string[] data = nb.Split (':');
99 if (data.Length < 2) {
100 log.Warn("NetBeagleQueryable: Ignoring improper NetBeagle entry: {0}", nb);
101 continue;
103 string host = data[0];
104 string port = data[1];
105 NetBeagleList.Add (new NetBeagleHandler (host, port, this));
108 if (NetBeagleList.Count > 0) {
109 NetBeagleListActive = true;
110 if (File.Exists (Path.Combine (PathFinder.StorageDir, NetBeagleConfigFile)))
112 log.Warn("NetBeagleQueryable: Duplicate configuration of networked Beagles detected!");
113 log.Info("NetBeagleQueryable: Remove '~/.beagle/netbeagle.cfg' file. Use 'beagle-config' instead to setup networked Beagle nodes.");
114 log.Info("Using ~/.beagle/config/networking.xml");
116 return;
120 //Fallback to ~/.beagle/netbeagle.cfg
121 if (!File.Exists (Path.Combine (PathFinder.StorageDir, NetBeagleConfigFile)))
122 return;
124 StreamReader reader = new StreamReader(Path.Combine
125 (PathFinder.StorageDir, NetBeagleConfigFile));
127 string entry;
128 while ( ((entry = reader.ReadLine ()) != null) && (entry.Trim().Length > 1)) {
130 if ((entry[0] != '#') && (entry.IndexOf(':') > 0)) {
131 string[] data = entry.Split (':');
132 if (data.Length < 2) {
133 log.Warn("NetBeagleQueryable: Ignoring improper NetBeagle entry: {0}", entry);
134 continue;
137 string host = data[0];
138 string port = data[1];
139 NetBeagleList.Add (new NetBeagleHandler (host, port, this));
143 if (NetBeagleList.Count > 0) {
144 NetBeagleListActive = true;
145 //log.Warn("NetBeagleQueryable: 'netbeagle.cfg' based configuration deprecated.\n Use 'beagle-config' or 'beagle-settings' instead to configure Networked Beagles");
146 log.Warn("NetBeagleQueryable: 'netbeagle.cfg' based configuration deprecated.\n Use 'beagle-config' instead to configure Networked Beagles");
150 private void NetBeagleConfigurationChanged (Conf.Section section)
152 Logger.Log.Info("NetBeagleConfigurationChanged EventHandler invoked");
153 if (! (section is Conf.NetworkingConfig))
154 return;
156 Conf.NetworkingConfig nc = (Conf.NetworkingConfig) section;
158 //if (nc.NetBeagleNodes.Count == 0)
159 // return;
161 ArrayList newList = new ArrayList();
162 foreach (string nb in nc.NetBeagleNodes) {
163 if (nb == null || nb == "")
164 continue;
165 string[] data = nb.Split (':');
166 if (data.Length < 2) {
167 log.Warn("NetBeagleQueryable: Ignoring improper NetBeagle entry: {0}", nb);
168 continue;
170 string host = data[0];
171 string port = data[1];
172 newList.Add (new NetBeagleHandler (host, port, this));
175 lock (this) {
176 NetBeagleList = newList;
177 NetBeagleListActive = (newList.Count == 0) ? false:true;
181 public bool AcceptQuery (Query query)
183 if (query.Text.Count <= 0)
184 return false;
186 if (! query.AllowsDomain (QueryDomain.Global))
187 return false;
189 return true;
192 public string GetSnippet (string[] query_terms, Hit hit)
194 string s = "";
196 if (hit is NetworkHit)
197 s = ((NetworkHit)hit).snippet;
199 return s;
202 public int GetItemCount ()
204 return -1;
207 public QueryableStatus GetQueryableStatus ()
210 QueryableStatus status = new QueryableStatus ();
211 return status;
214 public void DoQuery (Query query,IQueryResult result,
215 IQueryableChangeData changeData)
217 if (NetBeagleList.Count == 0)
218 return;
220 ArrayList resultHandleList = new ArrayList();
221 lock (NetBeagleList) {
222 log.Debug("NetBeagleQueryable: DoQuery ... Starting NetBeagleHandler queries");
223 foreach (NetBeagleHandler nb in NetBeagleList)
225 IAsyncResult iar = nb.DoQuery (query, result, changeData);
226 resultHandleList.Add (iar);
230 int i = 0;
231 foreach (IAsyncResult iar in resultHandleList)
232 while (! ((ReqContext)(iar.AsyncState)).RequestProcessed) {
233 Thread.Sleep(1000);
234 if (++i > 20) //Don't wait more than 20 secs
235 break;
238 log.Debug("NetBeagleQueryable:DoQuery ... Done");
241 /////////////////////////////////////////////////////////////////////////////////////////
242 //Methods related to checking & caching of networked search requests,
243 //to prevent duplicate queries in cascaded network operation
245 class TimerHopCount {
246 int ttl = 0;
247 int hops = 0;
249 public TimerHopCount (int h) {
250 this.hops = h;
251 this.ttl = RequestCacheTime;
254 public int TTL {
255 get {return ttl;}
256 set {ttl = value;}
259 public int Hops {
260 get {return hops;}
261 //set {hops = value;}
265 public static int AddRequest(Query q)
267 int searchId = 0;
268 lock (timerTable) {
270 if (requestTable.Contains(q))
271 return (int) requestTable[q];
273 searchId = System.Guid.NewGuid().GetHashCode();
275 if (searchId < 0)
276 searchId = -searchId;
278 requestTable.Add(q, searchId);
279 timerTable.Add(q, new TimerHopCount(1));
281 if (!cTimer.Enabled) {
282 cTimer.Start();
283 log.Debug("CachedRequestCleanupTimer started");
287 return searchId;
290 public static int HopCount(Query q)
292 int hops = -1;
294 lock (timerTable)
296 if (timerTable.Contains(q))
297 hops = ((TimerHopCount) timerTable[q]).Hops;
300 return hops;
303 public static void CacheRequest(Query q, int searchId, int hops)
305 lock (timerTable) {
307 if (requestTable.Contains(q)) {
308 requestTable[q] = searchId;
309 timerTable[q] = new TimerHopCount(hops);
311 else {
312 requestTable.Add(q, searchId);
313 timerTable.Add(q, new TimerHopCount(hops));
316 if (!cTimer.Enabled) {
317 cTimer.Start();
318 log.Info("CachedRequestCleanupTimer started");
321 log.Info("CacheRequest: HopCount = " + hops);
325 public static bool IsCachedRequest(int searchId)
327 bool cached = false;
329 lock (timerTable)
330 cached = requestTable.ContainsValue(searchId);
332 return cached;
335 private static void TimerEventHandler(object source, ElapsedEventArgs e)
337 int c = 0;
339 ArrayList keys = new ArrayList();
340 keys.AddRange(timerTable.Keys);
342 foreach (Query q in keys)
344 TimerHopCount thc = (TimerHopCount) timerTable[q];
345 c = thc.TTL;
346 if (c > 0)
347 c--;
349 if (c == 0)
350 RemoveRequest(q);
351 else
352 thc.TTL = c;
355 if ((c % 4) == 0) //Log status every 1 minute
356 log.Info("CachedRequestCleanupTimer-EventHandler: requestTable has {0} elements, Last entry count={1}", requestTable.Count, c);
358 if (timerTable.Count == 0) {
359 cTimer.Stop();
360 log.Info("Stopping CachedRequestCleanupTimer");
364 private static void RemoveRequest(Query q)
366 lock (timerTable) {
368 if (requestTable.Contains(q))
369 requestTable.Remove(q);
371 if (timerTable.Contains(q))
372 timerTable.Remove(q);