4 // Copyright (C) 2005 Novell, Inc.
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.
28 using System
.Diagnostics
;
30 using System
.Threading
;
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");
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
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");
111 last_item_count
= response
.ItemCount
;
113 if (response
.Receipts
!= null && response
.Receipts
.Length
> 0)
114 return response
.Receipts
;
119 public event IIndexerFlushHandler FlushEvent
;
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
142 RemoteIndexerRequest request
;
143 request
= new RemoteIndexerRequest ();
145 RemoteIndexerResponse response
;
146 response
= SendRequest (request
);
147 if (response
!= null)
148 last_item_count
= response
.ItemCount
;
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!");
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
);
184 } catch (IOException ex
) {
185 Logger
.Log
.Debug ("Caught IOException --- we probably need to launch a helper: {0}", ex
.Message
);
189 // If we caught an exception...
190 if (response
== null) {
191 if (! start_helper_by_hand
|| ! need_helper
)
194 if (start_helper_by_hand
) {
195 // Sleep briefly before trying again.
198 // Try to activate the helper.
204 if (response
== null && exception_count
>= 5)
205 Logger
.Log
.Error ("Exception limit exceeded trying to activate a helper. Giving up on indexing!");
210 /////////////////////////////////////////////////////////
212 static bool CheckHelper ()
214 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
216 if (storage_dir
== null)
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
))
225 // Open, and then immediately close, a connection to the helper's socket.
227 UnixClient test_client
;
228 test_client
= new UnixClient (socket_name
);
229 test_client
.Close ();
231 } catch (Exception ex
) {
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
)
246 // If a helper appears to be running, return immediately.
250 Logger
.Log
.Debug ("Launching helper process");
252 Process p
= new Process ();
253 p
.StartInfo
.UseShellExecute
= false;
254 p
.StartInfo
.FileName
= helper_path
;
257 // Poll the helper's socket. If the
263 found_helper
= CheckHelper ();
264 } while (poll_count
< 20
266 && ! Shutdown
.ShutdownRequested
);
269 throw new Exception ("Couldn't launch helper process");