FilterHtml.cs: Add ExtractText(string) command to extract text out of large html...
[beagle.git] / beagled / RemoteIndexer.cs
blob45b821d41398d578717182e0d6d301ec497c6e50
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;
31 using Mono.Unix;
33 using Beagle;
34 using Beagle.Util;
36 namespace Beagle.Daemon {
38 public class RemoteIndexer : IIndexer {
40 static string helper_path;
41 static int helper_pid = -1;
43 string remote_index_name;
44 int remote_index_minor_version = 0;
45 int last_item_count = -1;
47 static RemoteIndexer ()
49 string bihp = Environment.GetEnvironmentVariable ("_BEAGLED_INDEX_HELPER_PATH");
50 if (bihp == null)
51 throw new Exception ("_BEAGLED_INDEX_HELPER_PATH not set!");
53 helper_path = Path.GetFullPath (Path.Combine (bihp, "beagled-index-helper"));
54 if (! File.Exists (helper_path))
55 throw new Exception ("Could not find " + helper_path);
56 Logger.Log.Debug ("Found index helper at {0}", helper_path);
59 static public IIndexer NewRemoteIndexer (string name, int minor_version)
61 return new RemoteIndexer (name, minor_version);
64 public RemoteIndexer (string name, int minor_version)
66 this.remote_index_name = name;
67 this.remote_index_minor_version = minor_version;
70 public IndexerReceipt [] Flush (IndexerRequest request)
72 // If there isn't actually any work to do, just return
73 // an empty array.
74 if (request.IsEmpty)
75 return new IndexerReceipt [0];
77 // Iterate through the items in the IndexerRequest to
78 // store the streams before passing them to the helper.
79 foreach (Indexable indexable in request.Indexables) {
80 if (indexable.Type == IndexableType.Add)
81 indexable.StoreStream ();
84 RemoteIndexerRequest remote_request;
85 remote_request = new RemoteIndexerRequest ();
86 remote_request.RemoteIndexName = this.remote_index_name;
87 remote_request.RemoteIndexMinorVersion = this.remote_index_minor_version;
88 remote_request.Request = request;
90 RemoteIndexerResponse response;
91 response = SendRequest (remote_request);
93 if (response == null) {
94 Logger.Log.Error ("Something terrible happened --- Flush failed");
95 request.Cleanup ();
96 return null;
99 last_item_count = response.ItemCount;
101 return response.Receipts;
104 public int GetItemCount ()
106 if (last_item_count == -1) {
107 // Send an empty indexing request to cause the last item count to be
108 // initialized.
109 RemoteIndexerRequest request;
110 request = new RemoteIndexerRequest ();
112 RemoteIndexerResponse response;
113 response = SendRequest (request);
114 if (response != null)
115 last_item_count = response.ItemCount;
116 else
117 Logger.Log.Error ("Something terrible happened --- GetItemCount failed");
120 return last_item_count;
123 /////////////////////////////////////////////////////////
125 private RemoteIndexerResponse SendRequest (RemoteIndexerRequest request)
127 RemoteIndexerResponse response = null;
128 int exception_count = 0;
129 bool start_helper_by_hand = false;
131 if (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null)
132 start_helper_by_hand = true;
134 request.RemoteIndexName = remote_index_name;
135 request.RemoteIndexMinorVersion = remote_index_minor_version;
137 while (response == null
138 && exception_count < 5
139 && ! Shutdown.ShutdownRequested) {
141 bool need_helper = false;
143 //Logger.Log.Debug ("Sending request!");
144 try {
145 response = request.Send () as RemoteIndexerResponse;
146 //Logger.Log.Debug ("Done sending request");
147 } catch (ResponseMessageException ex) {
148 Logger.Log.Debug ("Caught ResponseMessageException: {0}", ex.Message);
150 if (ex.InnerException is System.Net.Sockets.SocketException) {
151 Logger.Log.Debug ("InnerException is SocketException -- we probably need to launch a helper");
152 need_helper = true;
153 } else if (ex.InnerException is IOException) {
154 Logger.Log.Debug ("InnerException is IOException -- we probably need to launch a helper");
155 need_helper = true;
156 } else {
157 Logger.Log.Debug (ex, "Exception was unexpected.");
161 // If we caught an exception...
162 if (response == null) {
163 if (! start_helper_by_hand || ! need_helper)
164 ++exception_count;
166 if (start_helper_by_hand) {
167 // Sleep briefly before trying again.
168 Thread.Sleep (1000);
169 } else {
170 // Try to activate the helper.
171 LaunchHelper ();
176 if (response == null && exception_count >= 5)
177 Logger.Log.Error ("Exception limit exceeded trying to activate a helper. Giving up on indexing!");
179 return response;
182 /////////////////////////////////////////////////////////
184 static bool CheckHelper ()
186 string storage_dir = PathFinder.GetRemoteStorageDir (false);
188 if (storage_dir == null)
189 return false;
191 // FIXME: We shouldn't need to know the path to the helper socket.
192 string socket_name = Path.Combine (storage_dir, "socket-helper");
194 if (! File.Exists (socket_name))
195 return false;
197 // Open, and then immediately close, a connection to the helper's socket.
198 try {
199 UnixClient test_client;
200 test_client = new UnixClient (socket_name);
201 test_client.Close ();
202 return true;
203 } catch (Exception ex) {
204 return false;
208 static object helper_lock = new object ();
210 static void LaunchHelper ()
212 // If we are in the process of shutting down, return immediately.
213 if (Shutdown.ShutdownRequested)
214 return;
216 lock (helper_lock) {
218 // If a helper appears to be running, return immediately.
219 if (CheckHelper ())
220 return;
222 Logger.Log.Debug ("Launching helper process");
224 SafeProcess p = new SafeProcess ();
225 p.Arguments = new string [] { helper_path };
226 p.Start ();
228 Logger.Log.Debug ("IndexHelper PID is {0}", p.Id);
230 // Poll the helper's socket. Wait up to a minute
231 // (500 ms * 120 times) for the helper to be ready
232 // to handle requests.
233 Stopwatch watch = new Stopwatch ();
234 watch.Start ();
235 int poll_count = 0;
236 bool found_helper;
237 do {
238 Thread.Sleep (500);
239 ++poll_count;
240 found_helper = CheckHelper ();
241 } while (poll_count < 120
242 && ! found_helper
243 && ! Shutdown.ShutdownRequested);
244 watch.Stop ();
246 if (! found_helper)
247 throw new Exception (String.Format ("Couldn't launch helper process {0}", p.Id));
249 Logger.Log.Debug ("Found IndexHelper ({0}) in {1}", p.Id, watch);
250 helper_pid = p.Id;
254 static public void SignalRemoteIndexer ()
256 // No helper running right now
257 if (helper_pid == -1 || ! CheckHelper ()) {
258 helper_pid = -1;
259 return;
262 Mono.Unix.Native.Syscall.kill (helper_pid, Mono.Unix.Native.Signum.SIGUSR1);