/// <summary> /// Handles the receipt of a response message coming from a remote platform by resuming the workflow run and handing the /// messages to the relevant CAST activity implementation. /// </summary> /// <param name="request">The original request message.</param> /// <param name="response">The response message to process.</param> public async void Process(TRequest request, TResponse response) { using (new DeferredChannelMessageContext()) using (CastService.GetCastContext()) { try { var run = EntityRepository.Get <WorkflowRun>(request.RunId, WorkflowRunPreload); if (run == null) { throw new Exception(string.Format("CAST workflow activity could not resume. Workflow run ({0}) not found.", request.RunId)); } // let the workflow or caching or whatever, catch up before officially responding to RabbitMQ if (run.RunStepCounter <= request.RunStep) { var state = await WaitForRunStep(run.Id, request.RunStep); if (state < request.RunStep) { throw new Exception(string.Format("CAST workflow activity could not resume. Workflow run ({0}) wasn't in step.", request.RunId)); } } var castEvent = new CastActivityResponseEvent <TRequest, TResponse>(request, response); WorkflowRunner.ResumeWorkflowAsync(run, castEvent); } catch (Exception err) { EventLog.Application.WriteError("Failed to process CAST response. {0}", err); } } }
/// <summary> /// Performs a simple write to the event log. /// </summary> /// <param name="logRequest">The log request.</param> /// <returns>The response to the request.</returns> public LogResponse Log(LogRequest logRequest) { using (Profiler.Measure("CastActivityService.Log")) { var response = new LogResponse(); try { if (logRequest == null) { throw new ArgumentNullException("logRequest"); } if (!CastService.GetIsCastConfigured()) { throw new InvalidOperationException(); } EventLog.Application.WriteInformation(logRequest.Message); response.Time = DateTime.UtcNow; } catch (Exception e) { response.IsError = true; response.Error = e.Message; } return(response); } }
/// <summary> /// Carries out an operation on a <see cref="UserAccount"/> within a tenant identified by the request. /// </summary> /// <param name="userRequest">The request.</param> /// <returns>The response.</returns> public UserInfoResponse UserOperation(UserOperationRequest userRequest) { using (Profiler.Measure("CastActivityService.UserOperation")) { var response = new UserInfoResponse { Users = new UserList() }; try { if (userRequest == null) { throw new ArgumentNullException("userRequest"); } if (!CastService.GetIsCastConfigured()) { throw new InvalidOperationException(); } var name = userRequest.User; var tenant = userRequest.Tenant; switch (userRequest.Operation) { case Operation.Create: UserService.Create(name, userRequest.Password, tenant, userRequest.Roles); break; case Operation.Delete: UserService.Delete(name, tenant); break; default: throw new NotSupportedException(userRequest.Operation.ToString()); } if (userRequest.Operation != Operation.Delete) { var u = UserService.GetUser(name, tenant); if (u != null) { response.Users.Add(u); } } } catch (Exception e) { response.IsError = true; response.Error = e.Message; } return(response); } }
/// <summary> /// Handles the receipt of a heartbeat message. /// </summary> /// <param name="pi">The platform information received.</param> private void HandleHeartbeat(RemotePlatformInfo pi) { if (!CastService.GetIsCastConfigured() || !CastService.GetIsCastServer()) { return; } using (new DeferredChannelMessageContext()) using (CastService.GetCastContext()) using (CastService.GetCastUser()) { PlatformService.CreateOrUpdate(pi); } }
/// <summary> /// Carries out an operation on an <see cref="App"/> with respect to the tenant identified by the request. /// </summary> /// <param name="appRequest">The request.</param> /// <returns>The response.</returns> public ApplicationInfoResponse ApplicationOperation(ApplicationOperationRequest appRequest) { using (Profiler.Measure("CastActivityService.ApplicationOperation")) { var response = new ApplicationInfoResponse(); try { if (appRequest == null) { throw new ArgumentNullException("appRequest"); } if (!CastService.GetIsCastConfigured()) { throw new InvalidOperationException(); } var tenant = appRequest.Tenant; switch (appRequest.Operation) { case Contracts.ApplicationOperation.Install: ApplicationService.Install(tenant, appRequest.Id, appRequest.Version); break; case Contracts.ApplicationOperation.Uninstall: ApplicationService.Uninstall(tenant, appRequest.Id); break; default: throw new NotSupportedException(appRequest.Operation.ToString()); } var apps = ApplicationService.GetInstalledApps(tenant); if (apps != null) { response.Installed = apps.ToList(); } } catch (Exception e) { response.IsError = true; response.Error = e.Message; } return(response); } }
/// <summary> /// Starts a timer which will send a heartbeat message every hour. /// </summary> private void StartHeartbeat() { var castConfiguration = ConfigurationSettings.GetCastConfigurationSection(); if (castConfiguration == null) { return; } var castSettings = castConfiguration.Cast; if (castSettings == null) { return; } if (castSettings.Enabled != true) { return; } var interval = castConfiguration.Cast.Heartbeat; if (interval < 0) { interval = 60; } EventLog.Application.WriteInformation("Sending heartbeat every {0} minutes.", interval); //CastService.SendHeartbeat(); _timer.Interval = interval * 1000 * 60; _timer.Elapsed += (s, a) => { try { EventLog.Application.WriteInformation("Sending heartbeat."); CastService.SendHeartbeat(); } catch (Exception ex) { EventLog.Application.WriteError("Unexpected failure starting CAST heartbeat on timer. {0}", ex); } }; _timer.Start(); }
/// <summary> /// Starts CAST communication channels. /// </summary> public void Start() { if (CastService.GetIsCastConfigured()) { EventLog.Application.WriteWarning("CAST communications are starting."); // Server specific if (CastService.GetIsCastServer()) { HeartbeatListener.Receive <RemotePlatformInfo>(SpecialStrings.CastHeartbeatKey, HandleHeartbeat, false); } SendHeartbeatNowListener.Subscribe <string>(SpecialStrings.CastHeartbeatDemandKey, SendHeartbeatNow); StartHeartbeat(); ClientListener.Respond <CastRequest, CastResponse>(CastService.GetClientCommunicationKey(), HandleRequest); } }
/// <summary> /// Handles a CAST request received, passing on to the appropriate CAST activities service call. /// </summary> /// <param name="request">The request object received.</param> /// <returns>The response to pass back to the CAST Server.</returns> private CastResponse HandleRequest(CastRequest request) { // direct the appropriate requests to the activity service (TODO: some kind of registration would be nice) if (request == null) { throw new ArgumentNullException("request"); } if (CastService.GetIsCastConfigured()) { using (new DeferredChannelMessageContext()) { // Log var logRequest = request as LogRequest; if (logRequest != null) { return(CastActivityService.Log(logRequest)); } // Tenant var tenantRequest = request as TenantOperationRequest; if (tenantRequest != null) { return(CastActivityService.TenantOperation(tenantRequest)); } // User var userRequest = request as UserOperationRequest; if (userRequest != null) { return(CastActivityService.UserOperation(userRequest)); } // Application var appRequest = request as ApplicationOperationRequest; if (appRequest != null) { return(CastActivityService.ApplicationOperation(appRequest)); } } } return(default(CastResponse)); }
/// <summary> /// Sends a heartbeat message immediately. /// </summary> /// <param name="request">The request received.</param> private void SendHeartbeatNow(string request) { EventLog.Application.WriteInformation("Sending heartbeat by request ({0})", request); CastService.SendHeartbeat(); }
/// <summary> /// Carries out an operation on a <see cref="Tenant"/> on this plaform identified by the request. /// </summary> /// <param name="tenantRequest">The request.</param> /// <returns>The response.</returns> public TenantInfoResponse TenantOperation(TenantOperationRequest tenantRequest) { using (Profiler.Measure("CastActivityService.TenantOperation")) { var response = new TenantInfoResponse { Tenants = new TenantList() }; try { if (tenantRequest == null) { throw new ArgumentNullException("tenantRequest"); } if (string.IsNullOrEmpty(tenantRequest.Name)) { throw new ArgumentException("Tenant name may not be empty."); } if (!CastService.GetIsCastConfigured()) { throw new InvalidOperationException(); } var name = tenantRequest.Name; switch (tenantRequest.Operation) { case Operation.Create: TenantService.Create(name); break; case Operation.Delete: TenantService.Delete(name); break; case Operation.Enable: TenantService.Enable(name); break; case Operation.Disable: TenantService.Disable(name); break; case Operation.Rename: TenantService.Rename(tenantRequest.Id, tenantRequest.Name); break; default: throw new NotSupportedException(tenantRequest.Operation.ToString()); } if (tenantRequest.Operation != Operation.Delete) { var ti = TenantService.GetTenant(name); if (ti != null) { response.Tenants.Add(ti); } } } catch (Exception e) { response.IsError = true; response.Error = e.Message; } return(response); } }