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
;
36 namespace Beagle
.Daemon
{
38 public class RemoteIndexer
: IIndexer
{
40 static string helper_path
;
42 string remote_index_name
;
43 int remote_index_minor_version
= 0;
44 int last_item_count
= -1;
46 static RemoteIndexer ()
48 string bihp
= Environment
.GetEnvironmentVariable ("_BEAGLED_INDEX_HELPER_PATH");
50 throw new Exception ("_BEAGLED_INDEX_HELPER_PATH not set!");
52 helper_path
= Path
.GetFullPath (Path
.Combine (bihp
, "beagled-index-helper"));
53 if (! File
.Exists (helper_path
))
54 throw new Exception ("Could not find " + helper_path
);
55 Logger
.Log
.Debug ("Found index helper at {0}", helper_path
);
58 static public IIndexer
NewRemoteIndexer (string name
, int minor_version
)
60 return new RemoteIndexer (name
, minor_version
);
63 public RemoteIndexer (string name
, int minor_version
)
65 this.remote_index_name
= name
;
66 this.remote_index_minor_version
= minor_version
;
69 public IndexerReceipt
[] Flush (IndexerRequest request
)
71 // If there isn't actually any work to do, just return
74 return new IndexerReceipt
[0];
76 // Iterate through the items in the IndexerRequest to
77 // store the streams before passing them to the helper.
78 foreach (Indexable indexable
in request
.Indexables
) {
79 if (indexable
.Type
== IndexableType
.Add
)
80 indexable
.StoreStream ();
83 RemoteIndexerRequest remote_request
;
84 remote_request
= new RemoteIndexerRequest ();
85 remote_request
.RemoteIndexName
= this.remote_index_name
;
86 remote_request
.RemoteIndexMinorVersion
= this.remote_index_minor_version
;
87 remote_request
.Request
= request
;
89 RemoteIndexerResponse response
;
90 response
= SendRequest (remote_request
);
92 if (response
== null) {
93 Logger
.Log
.Error ("Something terrible happened --- Flush failed");
98 last_item_count
= response
.ItemCount
;
100 return response
.Receipts
;
103 public int GetItemCount ()
105 if (last_item_count
== -1) {
106 // Send an empty indexing request to cause the last item count to be
108 RemoteIndexerRequest request
;
109 request
= new RemoteIndexerRequest ();
111 RemoteIndexerResponse response
;
112 response
= SendRequest (request
);
113 if (response
!= null)
114 last_item_count
= response
.ItemCount
;
116 Logger
.Log
.Error ("Something terrible happened --- GetItemCount failed");
119 return last_item_count
;
122 /////////////////////////////////////////////////////////
124 private RemoteIndexerResponse
SendRequest (RemoteIndexerRequest request
)
126 RemoteIndexerResponse response
= null;
127 int exception_count
= 0;
128 bool start_helper_by_hand
= false;
130 if (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null)
131 start_helper_by_hand
= true;
133 request
.RemoteIndexName
= remote_index_name
;
134 request
.RemoteIndexMinorVersion
= remote_index_minor_version
;
136 while (response
== null
137 && exception_count
< 5
138 && ! Shutdown
.ShutdownRequested
) {
140 bool need_helper
= false;
142 //Logger.Log.Debug ("Sending request!");
144 response
= request
.Send () as RemoteIndexerResponse
;
145 //Logger.Log.Debug ("Done sending request");
146 } catch (ResponseMessageException ex
) {
147 Logger
.Log
.Debug ("Caught ResponseMessageException: {0}", ex
.Message
);
149 if (ex
.InnerException
is System
.Net
.Sockets
.SocketException
) {
150 Logger
.Log
.Debug ("InnerException is SocketException -- we probably need to launch a helper");
152 } else if (ex
.InnerException
is IOException
) {
153 Logger
.Log
.Debug ("InnerException is IOException -- we probably need to launch a helper");
156 Logger
.Log
.Debug ("Exception was unexpected. Details: {0}", ex
);
160 // If we caught an exception...
161 if (response
== null) {
162 if (! start_helper_by_hand
|| ! need_helper
)
165 if (start_helper_by_hand
) {
166 // Sleep briefly before trying again.
169 // Try to activate the helper.
175 if (response
== null && exception_count
>= 5)
176 Logger
.Log
.Error ("Exception limit exceeded trying to activate a helper. Giving up on indexing!");
181 /////////////////////////////////////////////////////////
183 static bool CheckHelper ()
185 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
187 if (storage_dir
== null)
190 // FIXME: We shouldn't need to know the path to the helper socket.
191 string socket_name
= Path
.Combine (storage_dir
, "socket-helper");
193 if (! File
.Exists (socket_name
))
196 // Open, and then immediately close, a connection to the helper's socket.
198 UnixClient test_client
;
199 test_client
= new UnixClient (socket_name
);
200 test_client
.Close ();
202 } catch (Exception ex
) {
207 static object helper_lock
= new object ();
209 static void LaunchHelper ()
211 // If we are in the process of shutting down, return immediately.
212 if (Shutdown
.ShutdownRequested
)
217 // If a helper appears to be running, return immediately.
221 Logger
.Log
.Debug ("Launching helper process");
223 Process p
= new Process ();
224 p
.StartInfo
.UseShellExecute
= false;
225 p
.StartInfo
.FileName
= helper_path
;
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 ();
240 found_helper
= CheckHelper ();
241 } while (poll_count
< 120
243 && ! Shutdown
.ShutdownRequested
);
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
);