/// <summary> /// Default constructor /// </summary> /// <param name="db">The local database to replicate to/from</param> /// <param name="remote">The remote Uri to sync with</param> /// <param name="continuous">If set to <c>true</c> continuous.</param> /// <param name="clientFactory">The client factory for instantiating the HttpClient used to create web requests</param> /// <param name="workExecutor">The TaskFactory to execute work on</param> protected Replication(Database db, Uri remote, bool continuous, IHttpClientFactory clientFactory, TaskFactory workExecutor) { LocalDatabase = db; Continuous = continuous; // NOTE: Consider running a separate scheduler for all http requests. WorkExecutor = workExecutor; CancellationTokenSource = new CancellationTokenSource(); RemoteUrl = remote; Options = new ReplicationOptionsDictionary(); RequestHeaders = new Dictionary<String, Object>(); _requests = new ConcurrentDictionary<HttpRequestMessage, Task>(); // FIXME: Refactor to visitor pattern. if (RemoteUrl.GetQuery() != null && !StringEx.IsNullOrWhiteSpace(RemoteUrl.GetQuery())) { var uri = new Uri(remote.ToString()); var personaAssertion = URIUtils.GetQueryParameter(uri, PersonaAuthorizer.QueryParameter); if (personaAssertion != null && !StringEx.IsNullOrWhiteSpace(personaAssertion)) { var email = PersonaAuthorizer.RegisterAssertion(personaAssertion); var authorizer = new PersonaAuthorizer(email); Authenticator = authorizer; } var facebookAccessToken = URIUtils.GetQueryParameter(uri, FacebookAuthorizer.QUERY_PARAMETER); if (facebookAccessToken != null && !StringEx.IsNullOrWhiteSpace(facebookAccessToken)) { var email = URIUtils.GetQueryParameter(uri, FacebookAuthorizer.QUERY_PARAMETER_EMAIL); var authorizer = new FacebookAuthorizer(email); Uri remoteWithQueryRemoved = null; try { remoteWithQueryRemoved = new UriBuilder(remote.Scheme, remote.GetHost(), remote.Port, remote.AbsolutePath).Uri; } catch (UriFormatException e) { throw new ArgumentException("Invalid URI format.", "remote", e); } FacebookAuthorizer.RegisterAccessToken(facebookAccessToken, email, remoteWithQueryRemoved); Authenticator = authorizer; } // we need to remove the query from the URL, since it will cause problems when // communicating with sync gw / couchdb try { RemoteUrl = new UriBuilder(remote.Scheme, remote.GetHost(), remote.Port, remote.AbsolutePath).Uri; } catch (UriFormatException e) { throw new ArgumentException("Invalid URI format.", "remote", e); } } Batcher = new Batcher<RevisionInternal>(workExecutor, INBOX_CAPACITY, PROCESSOR_DELAY, inbox => { try { Log.V(TAG, "*** BEGIN ProcessInbox ({0} sequences)", inbox.Count); if(Continuous) { FireTrigger(ReplicationTrigger.Resume); } ProcessInbox (new RevisionList(inbox)); Log.V(TAG, "*** END ProcessInbox (lastSequence={0})", LastSequence); } catch (Exception e) { Log.E(TAG, "ProcessInbox failed: ", e); throw new RuntimeException(e); } }); ClientFactory = clientFactory; _stateMachine = new StateMachine<ReplicationState, ReplicationTrigger>(ReplicationState.Initial); InitializeStateMachine(); }
/// <summary> /// Default constructor /// </summary> /// <param name="db">The local database to replicate to/from</param> /// <param name="remote">The remote Uri to sync with</param> /// <param name="continuous">If set to <c>true</c> continuous.</param> /// <param name="clientFactory">The client factory for instantiating the HttpClient used to create web requests</param> /// <param name="workExecutor">The TaskFactory to execute work on</param> internal Replication(Database db, Uri remote, bool continuous, IHttpClientFactory clientFactory, TaskFactory workExecutor) { sessionID = $"repl{ Interlocked.Increment(ref _lastSessionID):000}"; var opts = new RemoteSessionContructorOptions { BaseUrl = remote, WorkExecutor = workExecutor, Id = _replicatorID, CancellationTokenSource = CancellationTokenSource }; _remoteSession = new RemoteSession(opts); Username = remote.UserInfo; LocalDatabase = db; _eventContext = LocalDatabase.Manager.CapturedContext; Continuous = continuous; // NOTE: Consider running a separate scheduler for all http requests. WorkExecutor = workExecutor; RemoteUrl = remote; #pragma warning disable 618 Options = new ReplicationOptionsDictionary(); #pragma warning restore 618 ReplicationOptions = new ReplicationOptions(); if (RemoteUrl.Query != null && !StringEx.IsNullOrWhiteSpace(RemoteUrl.Query)) { Authenticator = AuthenticatorFactory.CreateFromUri(remote); // we need to remove the query from the URL, since it will cause problems when // communicating with sync gw / couchdb try { RemoteUrl = new UriBuilder(remote.Scheme, remote.Host, remote.Port, remote.AbsolutePath).Uri; } catch (UriFormatException e) { throw Misc.CreateExceptionAndLog(Log.To.Sync, e, Tag, "Invalid URI format for remote endpoint"); } } Batcher = new Batcher<RevisionInternal>(workExecutor, INBOX_CAPACITY, ProcessorDelay, inbox => { try { Log.To.Sync.V(Tag, "*** {0} BEGIN ProcessInbox ({1} sequences)", this, inbox.Count); if(Continuous) { FireTrigger(ReplicationTrigger.Resume); } ProcessInbox (new RevisionList(inbox)); Log.To.Sync.V(Tag, "*** {0} END ProcessInbox (lastSequence={1})", this, LastSequence); } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Sync, e, Tag, "{0} ProcessInbox failed", this); } }); ClientFactory = clientFactory; _stateMachine = new StateMachine<ReplicationState, ReplicationTrigger>(ReplicationState.Initial); InitializeStateMachine(); }