//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Data;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{
///
/// Proxy class for the AgentOperators dialog and property pages.
/// Performs lazy instantiation of groups of data based around the operators dialog property pages
///
internal class AgentOperatorsData
{
#region members
///
/// Data container
///
CDataContainer dataContainer;
///
/// Original operator name. Empty if we are creating a new operator
///
string originalOperatorName = string.Empty;
///
/// Indicates whether we are creating an operator or not
///
bool createMode;
///
/// Has then data for the general page been initialised
///
bool generalInitialized = false;
///
/// has the data for the history page been initialised
///
bool historyInitialized = false;
///
/// True if this operator cannot be modified
///
bool readOnly = false;
#region general items
string name;
bool enabled;
string emailAddress;
string pagerAddress;
SqlServer.Management.Smo.Agent.WeekDays pagerDays;
DateTime weekdayStartTime;
DateTime weekdayEndTime;
DateTime saturdayStartTime;
DateTime saturdayEndTime;
DateTime sundayStartTime;
DateTime sundayEndTime;
#endregion
#region notification items
///
/// will be null if the alert notifications have not been initialised
///
IList alertNotifications;
///
/// will be null if the job notifications have not been initialised
///
IList jobNotifications;
#endregion
#region history items
DateTime lastEmailDate;
DateTime lastPagerDate;
#endregion
#endregion
#region properties
///
/// the current Operator SMO object
///
public Operator Operator
{
get
{
JobServer jobServer = GetJobServer();
return jobServer.Operators[this.originalOperatorName];
}
}
///
/// indicates if the data is in create mode
///
public bool Creating
{
get
{
return this.createMode;
}
}
///
/// name of the object
///
public string Name
{
get
{
LoadGeneralData();
return name;
}
set
{
LoadGeneralData();
name = value;
}
}
///
/// Indicates if the dataobject is readonly
///
public bool ReadOnly
{
get
{
return this.readOnly;
}
}
#region general items
///
/// indicates whether or not the operator is enabled
///
public bool Enabled
{
get
{
LoadGeneralData();
return enabled;
}
set
{
LoadGeneralData();
enabled = value;
}
}
///
/// email address of this operator
///
public string EmailAddress
{
get
{
LoadGeneralData();
return this.emailAddress;
}
set
{
LoadGeneralData();
this.emailAddress = value;
}
}
///
/// pager address of this operator
///
public string PagerAddress
{
get
{
LoadGeneralData();
return this.pagerAddress;
}
set
{
LoadGeneralData();
this.pagerAddress = value;
}
}
///
/// the days of the week the operator is active
///
public SqlServer.Management.Smo.Agent.WeekDays PagerDays
{
get
{
LoadGeneralData();
return this.pagerDays;
}
set
{
LoadGeneralData();
this.pagerDays = value;
}
}
///
/// Weekday start time for this operator to be active
///
public DateTime WeekdayStartTime
{
get
{
LoadGeneralData();
return this.weekdayStartTime;
}
set
{
LoadGeneralData();
this.weekdayStartTime = value;
}
}
///
/// Weekday end time for this operator to be active
///
public DateTime WeekdayEndTime
{
get
{
LoadGeneralData();
return this.weekdayEndTime;
}
set
{
LoadGeneralData();
this.weekdayEndTime = value;
}
}
///
/// Saturday start time for this operator to be active
///
public DateTime SaturdayStartTime
{
get
{
LoadGeneralData();
return this.saturdayStartTime;
}
set
{
LoadGeneralData();
this.saturdayStartTime = value;
}
}
///
/// Saturday end time for this operator to be active
///
public DateTime SaturdayEndTime
{
get
{
LoadGeneralData();
return this.saturdayEndTime;
}
set
{
LoadGeneralData();
this.saturdayEndTime = value;
}
}
///
/// Sunday start time for this operator to be active
///
public DateTime SundayStartTime
{
get
{
LoadGeneralData();
return this.sundayStartTime;
}
set
{
LoadGeneralData();
this.sundayStartTime = value;
}
}
///
/// Saturday end time for this operator to be active
///
public DateTime SundayEndTime
{
get
{
LoadGeneralData();
return this.sundayEndTime;
}
set
{
LoadGeneralData();
this.sundayEndTime = value;
}
}
#endregion
#region notification items
///
/// Alerts that notify this operator
///
public IList AlertNotifications
{
get
{
LoadAlertNotificationData();
return this.alertNotifications;
}
set
{
this.alertNotifications = value;
}
}
///
/// Jobs that notify this operator. This has to be set through the jobs dialog and is read only
///
public IList JobNotifications
{
get
{
LoadJobNotificationData();
return this.jobNotifications;
}
}
#endregion
#region history items
///
/// Date this operator was last emailed
///
public DateTime LastEmailDate
{
get
{
LoadHistoryData();
return this.lastEmailDate;
}
}
///
/// Date this operator was last paged
///
public DateTime LastPagerDate
{
get
{
LoadHistoryData();
return this.lastPagerDate;
}
}
#endregion
#endregion
#region Constructors
public AgentOperatorsData(CDataContainer dataContainer, string operatorName, bool createMode)
{
if (dataContainer == null)
{
throw new ArgumentNullException("dataContainer");
}
if (operatorName == null)
{
throw new ArgumentNullException("operatorName");
}
this.dataContainer = dataContainer;
this.readOnly = !this.dataContainer.Server.ConnectionContext.IsInFixedServerRole(FixedServerRoles.SysAdmin);
this.originalOperatorName = operatorName;
this.name = operatorName;
this.createMode = createMode;
}
#endregion
#region data loading
///
/// load data for the general tab. This can be called multiple times but will only load the data once intially
/// or after a reset
///
private void LoadGeneralData()
{
if(this.generalInitialized)
return;
// load defaults if we're creating
if(createMode)
{
LoadGeneralDefaults();
return;
}
// lookup the operator this will throw if it has been deleted.
Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator();
// setup the members
this.name = currentOperator.Name;
this.enabled = currentOperator.Enabled;
this.emailAddress = currentOperator.EmailAddress;
this.pagerAddress = currentOperator.PagerAddress;
this.pagerDays = currentOperator.PagerDays;
this.weekdayStartTime = ConvertAgentTime(currentOperator.WeekdayPagerStartTime);
this.weekdayEndTime = ConvertAgentTime(currentOperator.WeekdayPagerEndTime);
this.saturdayStartTime = ConvertAgentTime(currentOperator.SaturdayPagerStartTime);
this.saturdayEndTime = ConvertAgentTime(currentOperator.SaturdayPagerEndTime);
this.sundayStartTime = ConvertAgentTime(currentOperator.SundayPagerStartTime);
this.sundayEndTime = ConvertAgentTime(currentOperator.SundayPagerEndTime);
this.generalInitialized = true;
}
///
/// Load the data for the jobs that notify this operator. Can be called multiple times and will
/// only load the data initially, or after a reset.
///
private void LoadJobNotificationData()
{
if(this.jobNotifications != null)
return;
// just set defaults if we're creating as no jobs will point to this operator yet.
if(createMode)
{
LoadJobNotificationDefaults();
return;
}
JobServer jobServer = GetJobServer();
this.jobNotifications = new List();
// we have to loop through each job and see if it notifies us.
foreach(Job job in jobServer.Jobs)
{
bool emailOperator = (job.OperatorToEmail == this.originalOperatorName);
bool pageOperator = (job.OperatorToPage == this.originalOperatorName);
if(emailOperator || pageOperator )
{
// only return jobs that notify this operator
AgentJobNotificationHelper notification = new AgentJobNotificationHelper(job.Name
, job.EmailLevel
, job.PageLevel
);
this.jobNotifications.Add(notification);
}
}
}
///
/// Load alerts that notify this operator
///
private void LoadAlertNotificationData()
{
if (this.alertNotifications != null)
{
return;
}
// defaults in create ode
if (createMode)
{
LoadAlertNotificationDefaults();
return;
}
this.alertNotifications = new List();
Microsoft.SqlServer.Management.Smo.Agent.Operator agentOperator = GetCurrentOperator();
JobServer jobServer = GetJobServer();
// see all alerts that notifuy this operator
DataTable notifications = agentOperator.EnumNotifications();
DataRow alertRow;
bool notifyEmail;
bool notifyPager;
AgentAlertNotificationHelper alertNotification;
// Add every alert to the structure
foreach (Alert alert in jobServer.Alerts)
{
alertRow = null;
// see if the alert notifies us already
foreach (DataRow row in notifications.Rows)
{
if((string)row["AlertName"] == alert.Name)
{
alertRow = row;
break;
}
}
// set if the current alert notifies this operator
// if so so how
if (alertRow != null)
{
notifyEmail = (bool)alertRow["UseEmail"];
notifyPager = (bool)alertRow["UsePager"];
}
else
{ notifyEmail = false;
notifyPager = false;
}
alertNotification = new AgentAlertNotificationHelper(alert.Name
,notifyEmail
,notifyPager
,alert);
this.alertNotifications.Add(alertNotification);
}
}
///
/// load the notifiaction history for the operator
///
private void LoadHistoryData()
{
if (this.historyInitialized)
{
return;
}
if (this.createMode)
{
LoadHistoryDefaults();
return;
}
Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator();
this.lastEmailDate = currentOperator.LastEmailDate;
this.lastPagerDate = currentOperator.LastPagerDate;
}
#endregion
#region saving
///
/// apply any changes to the operator. If the operator does not exist create it.
///
public void ApplyChanges(AgentOperatorInfo operatorInfo)
{
// do nothing if we are read only
if (this.readOnly)
{
return;
}
JobServer jobServer = GetJobServer();
// get the operator. This will create a new one if it does not already exist
Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator();
// general tab
currentOperator.Enabled = operatorInfo.Enabled;
if (!string.IsNullOrWhiteSpace(operatorInfo.EmailAddress))
{
currentOperator.EmailAddress = operatorInfo.EmailAddress;
}
if (!string.IsNullOrWhiteSpace(operatorInfo.PagerAddress))
{
currentOperator.PagerAddress = operatorInfo.PagerAddress;
}
currentOperator.PagerDays = this.pagerDays;
if ((operatorInfo.PagerDays & Contracts.WeekDays.WeekDays) > 0)
{
currentOperator.WeekdayPagerStartTime = operatorInfo.WeekdayPagerStartTime;
currentOperator.WeekdayPagerEndTime = operatorInfo.WeekdayPagerEndTime;
}
if ((operatorInfo.PagerDays & Contracts.WeekDays.Saturday) > 0)
{
currentOperator.SaturdayPagerStartTime = operatorInfo.SaturdayPagerStartTime;
currentOperator.SaturdayPagerEndTime = operatorInfo.SaturdayPagerEndTime;
}
if ((operatorInfo.PagerDays & Contracts.WeekDays.Sunday) > 0)
{
currentOperator.SundayPagerStartTime = operatorInfo.SundayPagerStartTime;
currentOperator.SundayPagerEndTime = operatorInfo.SundayPagerEndTime;
}
if (this.createMode)
{
// create the object
currentOperator.Create();
this.originalOperatorName = this.name;
}
else
{
// alter the object
currentOperator.Alter();
}
// only set this up if the notifications has been set
if (this.alertNotifications != null)
{
SqlServer.Management.Smo.Agent.NotifyMethods notifyMethods;
for (int i = 0; i < alertNotifications.Count; ++i)
{
notifyMethods = 0;
if (alertNotifications[i].NotifyEmail)
{
notifyMethods |= SqlServer.Management.Smo.Agent.NotifyMethods.NotifyEmail;
}
if (alertNotifications[i].NotifyPager)
{
notifyMethods |= SqlServer.Management.Smo.Agent.NotifyMethods.Pager;
}
bool alertAlreadyNotifiesOperator = false;
// if we're not creating see if the current alert already notifies this operator
if (!createMode)
{
DataTable notifications = alertNotifications[i].Alert.EnumNotifications(this.originalOperatorName);
if (notifications.Rows.Count > 0)
{
alertAlreadyNotifiesOperator = true;
}
}
// either update or clear existing notifications
if (alertAlreadyNotifiesOperator)
{
if(notifyMethods != SqlServer.Management.Smo.Agent.NotifyMethods.None)
{
alertNotifications[i].Alert.UpdateNotification(this.originalOperatorName, notifyMethods);
}
else
{
alertNotifications[i].Alert.RemoveNotification(this.originalOperatorName);
}
}
else if(notifyMethods != SqlServer.Management.Smo.Agent.NotifyMethods.None)
{
// add a new notification
alertNotifications[i].Alert.AddNotification(this.originalOperatorName, notifyMethods);
}
}
}
// see if we need to rename. This has to be done last otherwise any scripts generated will be incorrect.
if (!this.createMode && currentOperator.Name != this.originalOperatorName)
{
currentOperator.Rename(this.name);
if(this.dataContainer.Server.ConnectionContext.SqlExecutionModes != SqlExecutionModes.CaptureSql)
{
this.originalOperatorName = this.name;
}
}
// update state if we aren't scripting
if (this.createMode && this.dataContainer.Server.ConnectionContext.SqlExecutionModes != SqlExecutionModes.CaptureSql)
{
this.createMode = false;
}
}
#endregion
#region reset
///
/// Reset the object to it's original state / reload any data from the erver
///
public void Reset()
{
JobServer jobServer = GetJobServer();
this.generalInitialized = false;
if (this.jobNotifications != null)
{
// ensure the individual jobs are reset also
jobServer.Jobs.Refresh(true);
this.jobNotifications = null;
}
if (this.alertNotifications != null)
{
// ensure the individual jobs are reset also
jobServer.Alerts.Refresh(true);
this.alertNotifications = null;
}
this.historyInitialized = false;
}
#endregion
#region defaults
///
/// set general tab defaults
///
private void LoadGeneralDefaults()
{
name = string.Empty;
this.emailAddress = string.Empty;
this.pagerAddress = string.Empty;
enabled = true;
pagerDays = 0;
weekdayStartTime = saturdayStartTime = sundayStartTime = new DateTime(2000, 1, 1, 8, 0, 0);
weekdayEndTime = saturdayEndTime = sundayEndTime = new DateTime(2000, 1, 1, 18, 0, 0);
this.generalInitialized = true;
}
///
/// Set job notification defaults. This is just an empty list
///
private void LoadJobNotificationDefaults()
{
this.jobNotifications = new List();
}
///
/// set the alert notification defaults. This list will contain all of the alerts
///
private void LoadAlertNotificationDefaults()
{
this.alertNotifications = new List();
JobServer jobServer = GetJobServer();
AgentAlertNotificationHelper alertNotification;
foreach(Alert alert in jobServer.Alerts)
{
alertNotification = new AgentAlertNotificationHelper(alert.Name, notifyEmail:false, notifyPager:false, alert: alert);
this.alertNotifications.Add(alertNotification);
}
}
///
/// load defaults for the history page
///
private void LoadHistoryDefaults()
{
this.lastEmailDate = DateTime.MinValue;
this.lastPagerDate = DateTime.MinValue;
this.historyInitialized = true;
}
#endregion
#region helpers
///
/// Get the job server. Will throw if it is not available
///
/// Job server object
private JobServer GetJobServer()
{
JobServer jobServer = this.dataContainer.Server.JobServer;
if (jobServer == null)
{
throw new ApplicationException(SR.JobServerIsNotAvailable);
}
return jobServer;
}
///
/// Get the current operator. If we are creating this will be a new operator. If we are modifying
/// an existing operator it will be the existing operator, and will throw if the operator has been
/// deleted.
///
/// Operator object
private Operator GetCurrentOperator()
{
JobServer jobServer = GetJobServer();
Operator currentOperator = jobServer.Operators[this.originalOperatorName];
this.createMode = (currentOperator == null);
if (this.createMode)
{
currentOperator = new Microsoft.SqlServer.Management.Smo.Agent.Operator(jobServer, this.name);
}
return currentOperator;
}
///
///
///
///
///
static public TimeSpan ConvertAgentTime(DateTime dateTime)
{
return new TimeSpan(dateTime.Hour, dateTime.Minute, dateTime.Second);
}
///
///
///
///
///
static public DateTime ConvertAgentTime(TimeSpan dateTime)
{
return new DateTime(2000, 1, 1, dateTime.Hours, dateTime.Minutes, dateTime.Seconds);
}
///
///
///
///
///
static public int ConvertAgentTimeToInt(DateTime dateTime)
{
return dateTime.Hour * 10000 + dateTime.Minute * 100 + dateTime.Second;
}
///
///
///
///
///
static public DateTime ConvertAgentTime(int dateTime)
{
return new DateTime(2000, 1, 1, (int)(dateTime / 10000), (int)((dateTime - (dateTime / 10000) * 10000) / 100), (int)(dateTime - (dateTime / 100) * 100));
}
#endregion
}
#region internal structures
///
/// Provides data to be consumed in the job notification grid
///
internal struct AgentJobNotificationHelper
{
///
/// constructor
///
/// job name
///
///
public AgentJobNotificationHelper(string name, CompletionAction notifyEmail, CompletionAction notifyPager)
{
this.Name = name;
this.NotifyEmail = notifyEmail;
this.NotifyPager = notifyPager;
}
///
/// Name of the job
///
public string Name;
///
/// job email notification action
///
public CompletionAction NotifyEmail;
///
/// job pager notification action
///
public CompletionAction NotifyPager;
}
///
/// Provides data to be consumed in the alert notification grid
///
internal struct AgentAlertNotificationHelper
{
///
/// constructor
///
/// Name of the alert
///
///
/// Alert object
public AgentAlertNotificationHelper(string name, bool notifyEmail, bool notifyPager, Alert alert)
{
this.Name = name;
this.NotifyEmail = notifyEmail;
this.NotifyPager = notifyPager;
this.Alert = alert;
}
///
/// Alert name
///
public string Name;
///
/// Indicates whether the alert will notify the operator through email
///
public bool NotifyEmail;
///
/// Indicates whether the alert will notify the operator through pager
///
public bool NotifyPager;
///
/// Alert object. optimisation to stop us having to lookup the alert object when needed
///
public Alert Alert;
}
#endregion
}