public virtual void Run() { running = true; HttpClient httpClient; if (client == null) { // This is a race condition that can be reproduced by calling cbpuller.start() and cbpuller.stop() // directly afterwards. What happens is that by the time the Changetracker thread fires up, // the cbpuller has already set this.client to null. See issue #109 Log.W(Log.TagChangeTracker, "%s: ChangeTracker run() loop aborting because client == null" , this); return; } if (mode == ChangeTracker.ChangeTrackerMode.Continuous) { // there is a failing unit test for this, and from looking at the code the Replication // object will never use Continuous mode anyway. Explicitly prevent its use until // it is demonstrated to actually work. throw new RuntimeException("ChangeTracker does not correctly support continuous mode" ); } httpClient = client.GetHttpClient(); backoff = new ChangeTrackerBackoff(); while (running) { Uri url = GetChangesFeedURL(); if (usePOST) { HttpPost postRequest = new HttpPost(url.ToString()); postRequest.SetHeader("Content-Type", "application/json"); StringEntity entity; try { entity = new StringEntity(ChangesFeedPOSTBody()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } postRequest.SetEntity(entity); request = postRequest; } else { request = new HttpGet(url.ToString()); } AddRequestHeaders(request); // Perform BASIC Authentication if needed bool isUrlBasedUserInfo = false; // If the URL contains user info AND if this a DefaultHttpClient then preemptively set the auth credentials string userInfo = url.GetUserInfo(); if (userInfo != null) { isUrlBasedUserInfo = true; } else { if (authenticator != null) { AuthenticatorImpl auth = (AuthenticatorImpl)authenticator; userInfo = auth.AuthUserInfo(); } } if (userInfo != null) { if (userInfo.Contains(":") && !userInfo.Trim().Equals(":")) { string[] userInfoElements = userInfo.Split(":"); string username = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[0]) : userInfoElements [0]; string password = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[1]) : userInfoElements [1]; Credentials credentials = new UsernamePasswordCredentials(username, password); if (httpClient is DefaultHttpClient) { DefaultHttpClient dhc = (DefaultHttpClient)httpClient; MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_285(credentials ); dhc.AddRequestInterceptor(preemptiveAuth, 0); } } else { Log.W(Log.TagChangeTracker, "RemoteRequest Unable to parse user info, not setting credentials" ); } } try { string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString(); maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@" , "://---:---@"); Log.V(Log.TagChangeTracker, "%s: Making request to %s", this, maskedRemoteWithoutCredentials ); HttpResponse response = httpClient.Execute(request); StatusLine status = response.GetStatusLine(); if (status.GetStatusCode() >= 300 && !Utils.IsTransientError(status)) { Log.E(Log.TagChangeTracker, "%s: Change tracker got error %d", this, status.GetStatusCode ()); this.error = new HttpResponseException(status.GetStatusCode(), status.GetReasonPhrase ()); Stop(); } HttpEntity entity = response.GetEntity(); InputStream input = null; if (entity != null) { try { input = entity.GetContent(); if (mode == ChangeTracker.ChangeTrackerMode.LongPoll) { // continuous replications IDictionary<string, object> fullBody = Manager.GetObjectMapper().ReadValue<IDictionary >(input); bool responseOK = ReceivedPollResponse(fullBody); if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK) { Log.V(Log.TagChangeTracker, "%s: Starting new longpoll", this); backoff.ResetBackoff(); continue; } else { Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (LongPoll)", this); Stop(); } } else { // one-shot replications JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory(); JsonParser jp = jsonFactory.CreateJsonParser(input); while (jp.NextToken() != JsonToken.StartArray) { } // ignore these tokens while (jp.NextToken() == JsonToken.StartObject) { IDictionary<string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue <IDictionary>(jp); if (!ReceivedChange(change)) { Log.W(Log.TagChangeTracker, "Received unparseable change line from server: %s", change ); } } Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (OneShot)", this); Stop(); break; } backoff.ResetBackoff(); } finally { try { entity.ConsumeContent(); } catch (IOException) { } } } } catch (Exception e) { if (!running && e is IOException) { } else { // in this case, just silently absorb the exception because it // frequently happens when we're shutting down and have to // close the socket underneath our read. Log.E(Log.TagChangeTracker, this + ": Exception in change tracker", e); } backoff.SleepAppropriateAmountOfTime(); } } Log.V(Log.TagChangeTracker, "%s: Change tracker run loop exiting", this); }
public virtual void Run() { running = true; HttpClient httpClient; if (client == null) { // This is a race condition that can be reproduced by calling cbpuller.start() and cbpuller.stop() // directly afterwards. What happens is that by the time the Changetracker thread fires up, // the cbpuller has already set this.client to null. See issue #109 Log.W(Log.TagChangeTracker, "%s: ChangeTracker run() loop aborting because client == null" , this); return; } if (mode == ChangeTracker.ChangeTrackerMode.Continuous) { // there is a failing unit test for this, and from looking at the code the Replication // object will never use Continuous mode anyway. Explicitly prevent its use until // it is demonstrated to actually work. throw new RuntimeException("ChangeTracker does not correctly support continuous mode" ); } httpClient = client.GetHttpClient(); backoff = new ChangeTrackerBackoff(); while (running) { Uri url = GetChangesFeedURL(); if (usePOST) { HttpPost postRequest = new HttpPost(url.ToString()); postRequest.SetHeader("Content-Type", "application/json"); StringEntity entity; try { entity = new StringEntity(ChangesFeedPOSTBody()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } postRequest.SetEntity(entity); request = postRequest; } else { request = new HttpGet(url.ToString()); } AddRequestHeaders(request); // Perform BASIC Authentication if needed bool isUrlBasedUserInfo = false; // If the URL contains user info AND if this a DefaultHttpClient then preemptively set the auth credentials string userInfo = url.GetUserInfo(); if (userInfo != null) { isUrlBasedUserInfo = true; } else { if (authenticator != null) { AuthenticatorImpl auth = (AuthenticatorImpl)authenticator; userInfo = auth.AuthUserInfo(); } } if (userInfo != null) { if (userInfo.Contains(":") && !userInfo.Trim().Equals(":")) { string[] userInfoElements = userInfo.Split(":"); string username = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[0]) : userInfoElements [0]; string password = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[1]) : userInfoElements [1]; Credentials credentials = new UsernamePasswordCredentials(username, password); if (httpClient is DefaultHttpClient) { DefaultHttpClient dhc = (DefaultHttpClient)httpClient; MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_285(credentials ); dhc.AddRequestInterceptor(preemptiveAuth, 0); } } else { Log.W(Log.TagChangeTracker, "RemoteRequest Unable to parse user info, not setting credentials" ); } } try { string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString(); maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@" , "://---:---@"); Log.V(Log.TagChangeTracker, "%s: Making request to %s", this, maskedRemoteWithoutCredentials ); HttpResponse response = httpClient.Execute(request); StatusLine status = response.GetStatusLine(); if (status.GetStatusCode() >= 300 && !Utils.IsTransientError(status)) { Log.E(Log.TagChangeTracker, "%s: Change tracker got error %d", this, status.GetStatusCode ()); this.error = new HttpResponseException(status.GetStatusCode(), status.GetReasonPhrase ()); Stop(); } HttpEntity entity = response.GetEntity(); InputStream input = null; if (entity != null) { try { input = entity.GetContent(); if (mode == ChangeTracker.ChangeTrackerMode.LongPoll) { // continuous replications IDictionary <string, object> fullBody = Manager.GetObjectMapper().ReadValue <IDictionary >(input); bool responseOK = ReceivedPollResponse(fullBody); if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK) { Log.V(Log.TagChangeTracker, "%s: Starting new longpoll", this); backoff.ResetBackoff(); continue; } else { Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (LongPoll)", this); Stop(); } } else { // one-shot replications JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory(); JsonParser jp = jsonFactory.CreateJsonParser(input); while (jp.NextToken() != JsonToken.StartArray) { } // ignore these tokens while (jp.NextToken() == JsonToken.StartObject) { IDictionary <string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue <IDictionary>(jp); if (!ReceivedChange(change)) { Log.W(Log.TagChangeTracker, "Received unparseable change line from server: %s", change ); } } Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (OneShot)", this); Stop(); break; } backoff.ResetBackoff(); } finally { try { entity.ConsumeContent(); } catch (IOException) { } } } } catch (Exception e) { if (!running && e is IOException) { } else { // in this case, just silently absorb the exception because it // frequently happens when we're shutting down and have to // close the socket underneath our read. Log.E(Log.TagChangeTracker, this + ": Exception in change tracker", e); } backoff.SleepAppropriateAmountOfTime(); } } Log.V(Log.TagChangeTracker, "%s: Change tracker run loop exiting", this); }