//Note: This method changes the state of a static class. So results may not be as expected in multi-threaded scenarios. internal void InvokeTestHookInitializeMethod(Type type) { if (WebUtil.IsFriendClass(type)) { const BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly; MethodInfo info = type.GetMethod("InitializeConfigurationTestHook", bindingAttr, null, new[] { typeof(ISyncServiceConfiguration) }, null); if ((info != null) && (info.ReturnType == typeof(void))) { ParameterInfo[] parameters = info.GetParameters(); if ((parameters.Length == 1) && !parameters[0].IsOut) { var objArray = new object[] { this }; try { info.Invoke(null, objArray); } catch (TargetInvocationException exception) { SyncTracer.Warning("Exception invoking the TestHookInitialization method. Details {0}", WebUtil.GetExceptionMessage(exception)); ErrorHandler.HandleTargetInvocationException(exception); throw; } return; } } } }
/// <summary> /// Delegate passed into the custom body writer to form the outgoing response. /// </summary> /// <param name="writer"></param> /// <param name="syncWriter"></param> private static void WriteResponse(XmlDictionaryWriter writer, SyncWriter syncWriter) { try { syncWriter.WriteFeed(writer); } catch (Exception exception) { // An exception at this point seems to be unrecoverable but ideally we should not hit exceptions since we are only // writing to the XmlDictionaryWriter. SyncServiceTracer.TraceError("Exception in WriteResponse method. Details: {0}", WebUtil.GetExceptionMessage(exception)); } }
/// <summary> /// Invokes the InitializeService user method. /// </summary> /// <param name="type">service type (used for reflection)</param> private void InvokeStaticInitialization(Type type) { // Search for the InitializeService method going from most-specific to least-specific type. const BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly; while (type != null) { MethodInfo info = type.GetMethod("InitializeService", bindingAttr, null, new[] { typeof(ISyncServiceConfiguration) }, null); if ((info != null) && (info.ReturnType == typeof(void))) { ParameterInfo[] parameters = info.GetParameters(); if ((parameters.Length == 1) && !parameters[0].IsOut) { var objArray = new object[] { this }; try { info.Invoke(null, objArray); return; } catch (TargetInvocationException exception) { SyncTracer.Warning("Exception invoking the static InitializeService method. Details {0}", WebUtil.GetExceptionMessage(exception)); ErrorHandler.HandleTargetInvocationException(exception); throw; } } } type = type.BaseType; } // We should never exit from here when the InitializeService method is implemented. throw SyncServiceException.CreateInternalServerError(Strings.InitializeServiceMethodNotImplemented); }
/// <summary>Check whether a connection can be opened successfully to the database.</summary> /// <param name="configuration">Service configuration</param> /// <returns>Result of the diagnostic check</returns> private static DiagTestResult CheckSqlConnection(SyncServiceConfiguration configuration) { var result = new DiagTestResult(); try { new SqlConnectionStringBuilder(configuration.ServerConnectionString); } catch (KeyNotFoundException keyNotFoundException) { result.TestResult = DiagConstants.INVALID_SQL_CONNECTION_STRING; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(keyNotFoundException); } } catch (FormatException formatException) { result.TestResult = DiagConstants.INVALID_SQL_CONNECTION_STRING; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(formatException); } } catch (ArgumentException argumentException) { result.TestResult = DiagConstants.INVALID_SQL_CONNECTION_STRING; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(argumentException); } } catch (Exception e) { result.TestResult = DiagConstants.UNKNOWN_ERROR; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(e); } } if (result.TestResult == DiagConstants.NOT_DETERMINED) { try { using (var connection = new SqlConnection(configuration.ServerConnectionString)) { connection.Open(); } result.TestResult = DiagConstants.SUCCESS; } catch (InvalidOperationException invalidOperationException) { result.TestResult = DiagConstants.ERROR_OPENING_SQL_CONNECTION; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(invalidOperationException); } } catch (SqlException sqlException) { result.TestResult = DiagConstants.ERROR_OPENING_SQL_CONNECTION; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(sqlException); } } catch (ArgumentException argumentException) { result.TestResult = DiagConstants.ERROR_OPENING_SQL_CONNECTION; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(argumentException); } } catch (Exception e) { result.TestResult = DiagConstants.UNKNOWN_ERROR; if (IsLocalRequest) { result.ExceptionDetails = WebUtil.GetExceptionMessage(e); } } } return(result); }
// This method ensures that we have a valid feed through the formatters. The BodyWriter delegate in WCF // seems to not recover from an unhandled exception caused by the formatters and sends out an empty response to the caller. // This is a workaround for this issue until we find a better solution. private SyncWriter GetSyncWriterWithContents() { var conflictEntryKeys = new List <string>(); var errorEntryKeys = new List <string>(); var primaryKeyToIncomingEntitiesMapping = new Dictionary <string, IOfflineEntity>(); // Save the mapping between entity PK string -> entity foreach (var entity in _incomingEntities) { string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity); if (primaryKeyToIncomingEntitiesMapping.ContainsKey(primaryKey)) { throw SyncServiceException.CreateInternalServerError(Strings.MultipleEntriesWithSamePrimaryKeyInIncomingRequest); } primaryKeyToIncomingEntitiesMapping.Add(primaryKey, entity); } if (_rejectedEntities != null) { foreach (var entity in _rejectedEntities.Keys) { string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity); if (primaryKeyToIncomingEntitiesMapping.ContainsKey(primaryKey)) { throw SyncServiceException.CreateInternalServerError(Strings.MultipleEntriesWithSamePrimaryKeyInIncomingRequest); } primaryKeyToIncomingEntitiesMapping.Add(primaryKey, entity); } } // Get the appropriate SyncWriter instance based on the serialization format. var oDataWriter = WebUtil.GetSyncWriter(_responseSerializationFormat, _baseUri); oDataWriter.StartFeed(_applyChangesResponse.IsLastBatch, _applyChangesResponse.ServerBlob); // Write conflict entities. foreach (var entity in _applyChangesResponse.Conflicts) { // Add the primary key string to the conflictEntryKey list. // The primary keys are the same for both Live and Losing entities. conflictEntryKeys.Add(ReflectionUtility.GetPrimaryKeyString(entity.LiveEntity)); string tempId; // If the client change lost, then we need to set the Id property // only if the property was not null/empty in the incoming request. string entityId = WebUtil.GenerateOfflineEntityId(entity.LiveEntity); // Set the Id property of the Live entity (server's copy). entity.LiveEntity.ServiceMetadata.Id = entityId; // Set the Id property of the Losing entity to the incoming entity's Id value entity.LosingEntity.ServiceMetadata.Id = entityId; // get the original tempId. Null value is ok. _idToTempIdMapping.TryGetValue(entityId, out tempId); if (entity.Resolution == SyncConflictResolution.ServerWins) { // The losing entity is the client's copy. // When resolution is ServerWins, we only need to set the losing change tempId. oDataWriter.AddConflictItem(entity.LiveEntity, null /*tempId*/, entity.LosingEntity, tempId, entity.Resolution); } // If the client change won, then just set the Id property since an insert would have succeeded. else { // When resolution is ClientWins, we only need to set the LiveEntity tempId. oDataWriter.AddConflictItem(entity.LiveEntity, tempId, entity.LosingEntity, null /* tempId */, entity.Resolution); } } // Write error entities. foreach (var syncError in _applyChangesResponse.Errors) { Debug.Assert(null != syncError.LiveEntity); Debug.Assert(null != syncError.ErrorEntity); string entityId = WebUtil.GenerateOfflineEntityId(syncError.LiveEntity); // Set the Id for Live and Losing entity. syncError.LiveEntity.ServiceMetadata.Id = entityId; syncError.ErrorEntity.ServiceMetadata.Id = entityId; string primaryKeyString = ReflectionUtility.GetPrimaryKeyString(syncError.ErrorEntity); // Add the string to the error key list. errorEntryKeys.Add(primaryKeyString); string tempId; _idToTempIdMapping.TryGetValue(entityId, out tempId); oDataWriter.AddErrorItem(syncError.LiveEntity, syncError.ErrorEntity, tempId, syncError.Description); } // Write all the inserted records here by iterating over the _incomingNewInsertEntities list foreach (var entity in _incomingNewInsertEntities) { string entityTempId; // Get the tempId of the entity. _idToTempIdMapping.TryGetValue(WebUtil.GenerateOfflineEntityId(entity), out entityTempId); // Write the output to the SyncWriter. oDataWriter.AddItem(entity, entityTempId); } return(oDataWriter); }
public Stream ProcessRequest(HttpContextServiceHost ctx) { DateTime startTime = DateTime.Now; bool logged = false; Exception raisedException = null; try { try { // Intialize the service host first, since most of the logic depends on it. _serviceHost = ctx;// new HttpContextServiceHost(messageBody); //_serviceHost.ValidateRequestHttpVerbAndSegments(); // Create the configuration. The first call will initialize the configuration // and all other calls will be a no-op. CreateConfiguration(CurrentScope()); // Raise event for user code InvokeOnSyncRequestStart(); _requestDescription = new RequestParser(_serviceHost, _syncConfiguration).ParseIncomingRequest(); string email; Guid userId = Logon(ctx, out email); ctx.UserId = userId.ToString(); ctx.UserEMail = email; Common.Logon.CheckLicense(CurrentScope(), ctx.Headers["deviceId"]); Common.Logon.CheckCoreVersion(CurrentScope(), ctx.Headers["coreversion"]); ctx.ResourceVersion = Common.Logon.GetResourceVersion(CurrentScope()); //add request parameters _requestDescription.RequestParams = new Dictionary <string, object>(); _requestDescription.RequestParams.Add("@UserId", userId); if (null == _requestDescription.SyncBlob || 0 == _requestDescription.SyncBlob.Length) { InitializeNewClient(userId, "Default"); } _requestProcessor = RequestProcessorFactory.GetRequestProcessorInstance(_requestDescription.RequestCommand, _syncConfiguration, _serviceHost); _outgoingMessage = _requestProcessor.ProcessRequest(_requestDescription); // Add sync properties var responseProperties = _outgoingMessage.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; if (null != responseProperties) { responseProperties.Headers[SyncServiceConstants.SYNC_SERVICE_VERSION_KEY] = SyncServiceConstants.SYNC_SERVICE_VERSION_VALUE; responseProperties.Headers[SyncServiceConstants.SYNC_SERVICE_USERID] = userId.ToString(); } // Raise event for user code InvokeOnEndSyncRequest(_outgoingMessage); } catch (SyncServiceException syncServiceException) { raisedException = syncServiceException; ProcessSyncServiceException(ctx, syncServiceException); //_outgoingMessage is set inside } catch (DbSyncException dbSyncException) { raisedException = dbSyncException; if (dbSyncException.Message.StartsWith("Cannot find a valid scope")) { _outgoingMessage = CreateExceptionMessage(ctx, HttpStatusCode.Conflict, dbSyncException.Message); } else { _outgoingMessage = CreateExceptionMessageEx(dbSyncException, ctx); } } catch (Exception exception) { if (WebUtil.IsFatalException(exception)) { throw; } raisedException = exception; if (_outgoingMessage == null) { _outgoingMessage = CreateExceptionMessageEx(exception, ctx); } if (_outgoingMessage == null) { _outgoingMessage = CreateMessageFromUnhandledException(ctx, exception); } } return(MessageToStream(_outgoingMessage, ctx)); } finally { if (!logged) { logged = LogRequestInfo(startTime, ctx.Headers); } if (raisedException != null) { LogException(raisedException); } LogToDb(_requestDescription.RequestCommand, ctx, startTime, ctx.Headers, raisedException); } }
/// <summary> /// Read and parse the incoming request stream for a POST request. /// </summary> private void ReadIncomingRequestStreamForPost() { if (null == _serviceHost.RequestStream || !_serviceHost.RequestStream.CanRead) { SyncTracer.Info("Request stream for HTTP POST is empty, null or cannot be read."); return; } try { var reader = WebUtil.GetSyncReader(_serviceHost.GetRequestContentSerializationFormat(), _serviceHost.RequestStream, _configuration.TypeToTableGlobalNameMapping.Keys.ToArray()); reader.Start(); while (reader.Next()) { switch (reader.ItemType) { case ReaderItemType.Entry: IOfflineEntity entity = reader.GetItem(); if (entity.ServiceMetadata.IsTombstone) { if (String.IsNullOrEmpty(entity.ServiceMetadata.Id)) { throw SyncServiceException.CreateBadRequestError(Strings.TombstoneEntityHasNoId); } WebUtil.ParseIdStringAndPopulateKeyFields(entity, _serviceHost.ServiceBaseUri); } _entityList.Add(entity); bool hasTempId = false; if (reader.HasTempId()) { // Save the entity id to tempId mapping for use later when writing response. _idToTempIdMapping.Add(WebUtil.GenerateOfflineEntityId(entity), reader.GetTempId()); hasTempId = true; } // Make sure, we have atleast one of Id or TempId if (String.IsNullOrEmpty(entity.ServiceMetadata.Id) && !hasTempId) { throw SyncServiceException.CreateBadRequestError(Strings.BothIdAndTempIdAreMissing); } break; case ReaderItemType.SyncBlob: _syncBlob = reader.GetServerBlob(); break; } } } catch (XmlException exception) { SyncTracer.Warning("XmlException: {0}", WebUtil.GetExceptionMessage(exception)); throw SyncServiceException.CreateBadRequestError(Strings.BadRequestPayload); } }