Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / RemoteIndexer.cs
blobb80802ed84fad8e1a8e4dd6272bfa2316cf7e621
1 //
2 // RemoteIndexer.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Diagnostics;
29 using System.IO;
30 using System.Threading;
32 using Beagle;
33 using Beagle.Util;
35 namespace Beagle.Daemon {
37 public class RemoteIndexer : IIndexer {
39 static string helper_path;
41 string remote_index_name;
42 int remote_index_minor_version = 0;
43 int last_item_count = -1;
45 RemoteIndexerRequest pending_request = new RemoteIndexerRequest ();
46 object pending_request_lock = new object ();
48 static RemoteIndexer ()
50 string bihp = Environment.GetEnvironmentVariable ("_BEAGLED_INDEX_HELPER_PATH");
51 if (bihp == null)
52 throw new Exception ("_BEAGLED_INDEX_HELPER_PATH not set!");
54 helper_path = Path.GetFullPath (Path.Combine (bihp, "beagled-index-helper"));
55 if (! File.Exists (helper_path))
56 throw new Exception ("Could not find " + helper_path);
57 Logger.Log.Debug ("Found index helper at {0}", helper_path);
60 static public IIndexer NewRemoteIndexer (string name, int minor_version)
62 return new RemoteIndexer (name, minor_version);
65 public RemoteIndexer (string name, int minor_version)
67 this.remote_index_name = name;
68 this.remote_index_minor_version = minor_version;
71 public void Add (Indexable indexable)
73 indexable.StoreStream ();
74 lock (pending_request_lock)
75 pending_request.Add (indexable);
78 public void Remove (Uri uri)
80 lock (pending_request_lock)
81 pending_request.Remove (uri);
84 public void Optimize ()
86 pending_request.OptimizeIndex = true;
89 public IndexerReceipt [] FlushAndBlock ()
91 RemoteIndexerRequest flushed_request = null;
92 lock (pending_request) {
94 // If there isn't actually any work to do, just return
95 // an empty array.
96 if (pending_request.IsEmpty)
97 return new IndexerReceipt [0];
99 flushed_request = pending_request;
100 pending_request = new RemoteIndexerRequest ();
103 RemoteIndexerResponse response;
104 response = SendRequest (flushed_request);
106 if (response == null) {
107 Logger.Log.Error ("Something terrible happened --- Flush failed");
108 return null;
111 last_item_count = response.ItemCount;
113 if (response.Receipts != null && response.Receipts.Length > 0)
114 return response.Receipts;
115 else
116 return null;
119 public event IIndexerFlushHandler FlushEvent;
121 public void Flush ()
123 // FIXME: Right now we don't support a non-blocking flush,
124 // but it would be easy enough to do it in a thread.
126 IndexerReceipt [] receipts;
128 receipts = FlushAndBlock ();
130 if (FlushEvent != null) {
131 if (receipts != null)
132 FlushEvent (this, receipts);
133 FlushEvent (this, null); // all done
137 public int GetItemCount ()
139 if (last_item_count == -1) {
140 // Send an empty indexing request to cause the last item count to be
141 // initialized.
142 RemoteIndexerRequest request;
143 request = new RemoteIndexerRequest ();
145 RemoteIndexerResponse response;
146 response = SendRequest (request);
147 if (response != null)
148 last_item_count = response.ItemCount;
149 else
150 Logger.Log.Error ("Something terrible happened --- GetItemCount failed");
152 return last_item_count;
155 /////////////////////////////////////////////////////////
157 private RemoteIndexerResponse SendRequest (RemoteIndexerRequest request)
159 RemoteIndexerResponse response = null;
160 int exception_count = 0;
161 bool start_helper_by_hand = false;
163 if (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null)
164 start_helper_by_hand = true;
166 request.RemoteIndexName = remote_index_name;
167 request.RemoteIndexMinorVersion = remote_index_minor_version;
169 while (response == null
170 && exception_count < 5
171 && ! Shutdown.ShutdownRequested) {
173 bool need_helper = false;
175 //Logger.Log.Debug ("Sending request!");
176 try {
177 response = request.Send () as RemoteIndexerResponse;
178 Logger.Log.Debug ("Done sending request");
179 } catch (ResponseMessageException ex) {
180 Logger.Log.Debug ("Caught ResponseMessageException: {0}", ex.Message);
181 } catch (System.Net.Sockets.SocketException ex) {
182 Logger.Log.Debug ("Caught SocketException -- we probably need to launch a helper: {0}", ex.Message);
183 need_helper = true;
184 } catch (IOException ex) {
185 Logger.Log.Debug ("Caught IOException --- we probably need to launch a helper: {0}", ex.Message);
186 need_helper = true;
189 // If we caught an exception...
190 if (response == null) {
191 if (! start_helper_by_hand || ! need_helper)
192 ++exception_count;
194 if (start_helper_by_hand) {
195 // Sleep briefly before trying again.
196 Thread.Sleep (1000);
197 } else {
198 // Try to activate the helper.
199 LaunchHelper ();
204 if (response == null && exception_count >= 5)
205 Logger.Log.Error ("Exception limit exceeded trying to activate a helper. Giving up on indexing!");
207 return response;
210 /////////////////////////////////////////////////////////
212 static bool CheckHelper ()
214 string storage_dir = PathFinder.GetRemoteStorageDir (false);
216 if (storage_dir == null)
217 return false;
219 // FIXME: We shouldn't need to know the path to the helper socket.
220 string socket_name = Path.Combine (storage_dir, "socket-helper");
222 if (! File.Exists (socket_name))
223 return false;
225 // Open, and then immediately close, a connection to the helper's socket.
226 try {
227 UnixClient test_client;
228 test_client = new UnixClient (socket_name);
229 test_client.Close ();
230 return true;
231 } catch (Exception ex) {
232 return false;
236 static object helper_lock = new object ();
238 static void LaunchHelper ()
240 // If we are in the process of shutting down, return immediately.
241 if (Shutdown.ShutdownRequested)
242 return;
244 lock (helper_lock) {
246 // If a helper appears to be running, return immediately.
247 if (CheckHelper ())
248 return;
250 Logger.Log.Debug ("Launching helper process");
252 Process p = new Process ();
253 p.StartInfo.UseShellExecute = false;
254 p.StartInfo.FileName = helper_path;
255 p.Start ();
257 // Poll the helper's socket. If the
258 int poll_count = 0;
259 bool found_helper;
260 do {
261 Thread.Sleep (200);
262 ++poll_count;
263 found_helper = CheckHelper ();
264 } while (poll_count < 20
265 && ! found_helper
266 && ! Shutdown.ShutdownRequested);
268 if (! found_helper)
269 throw new Exception ("Couldn't launch helper process");