private void ParseJobXml(ref DigitalSendServerJobLogger log, string xmlJobInfo)
 {
     if (!string.IsNullOrEmpty(xmlJobInfo))
     {
         ParseJobXml(ref log, XmlUtil.CreateXDocument(xmlJobInfo));
     }
 }
        /// <summary>
        /// Checks to make sure we have a valid SessionId before attempting to log.
        /// </summary>
        /// <param name="log">The DigitalSendServerJobLogger.</param>
        private void SubmitLog(DigitalSendServerJobLogger log)
        {
            if (log.SessionId.Length != 8)
            {
                TraceFactory.Logger.Error($"Invalid Session Id: {log.SessionId}.  FileName: {log.FileName}.");
                return;
            }

            _dataLogger.SubmitAsync(log);
        }
        private DigitalSendServerJobLogger CreateLogFromDevice(IDataRecord reader)
        {
            DigitalSendServerJobLogger log = new DigitalSendServerJobLogger((Guid)reader["Id"]);

            log.JobType       = reader["JobType"] as string;
            log.FileName      = GetFileName(reader);
            log.FileSizeBytes = reader["DeliveredFileSize"] as int?;
            ParseJobXml(ref log, reader["XmlJobInfo"] as string);
            log.CompletionDateTime = ApplyLocalOffset((DateTime)reader["CompletionTime"], log.DssVersion);

            // Figure out the session ID
            log.SessionId = GetSessionId(reader);

            return(log);
        }
        private DigitalSendServerJobLogger CreateLogForNotFound(Guid jobId, string logText)
        {
            DigitalSendServerJobLogger log = new DigitalSendServerJobLogger(jobId);

            log.CompletionDateTime = DateTime.Now;
            log.JobType            = logText;
            log.CompletionStatus   = logText;
            log.FileName           = logText;
            log.FileType           = logText;
            log.DssVersion         = logText;
            log.ProcessedBy        = logText;
            log.DeviceModel        = logText;

            // Use the last session ID we logged
            log.SessionId = _sessionId;

            return(log);
        }
        private DigitalSendServerJobLogger CreateLogFromMaster(IDataRecord reader)
        {
            DigitalSendServerJobLogger log = new DigitalSendServerJobLogger((Guid)reader["JobId"]);

            log.JobType            = reader["JobType"] as string;
            log.DssVersion         = reader["DssSoftwareVersion"] as string;
            log.CompletionDateTime = ApplyLocalOffset((DateTime)reader["CompletionTime"], log.DssVersion);
            log.CompletionStatus   = reader["JobCompletionStatus"] as string;
            log.FileName           = GetFileName(reader);
            log.FileSizeBytes      = reader["DeliveredFileSize"] as int?;
            log.FileType           = reader["FileType"] as string;
            log.ScannedPages       = (short?)(reader["ScannedPages"] as int?);

            log.ProcessedBy = reader["ProcessedBy"] as string;
            log.DeviceModel = reader["DeviceModel"] as string;

            // Figure out the session ID
            log.SessionId = GetSessionId(reader);

            return(log);
        }
        /// <summary>
        /// The XML data received from the device table seems to be inconsistent at best.
        /// For the purpose of populating the server log, we'll populate what we can,
        /// ignoring all errors that may be thrown while attempting to retrieve the data,
        /// marking them as "unavailable" as a flag that an attempt was made to retrieve the data.
        /// </summary>
        /// <param name="log"></param>
        /// <param name="xmlJobInfo"></param>
        private void ParseJobXml(ref DigitalSendServerJobLogger log, XDocument xmlJobInfo)
        {
            string        unavailable = "<unavailable>";
            StringBuilder message     = null;

            XElement headerElement = xmlJobInfo.Descendants("HeaderElements").FirstOrDefault();

            if (headerElement != null)
            {
                try
                {
                    log.DeviceModel = headerElement.Element("cDeviceDescription").Value;
                }
                catch
                {
                    log.DeviceModel = unavailable;
                }
                try
                {
                    log.CompletionStatus = headerElement.Element("cJobStatusAppHeading").Value;
                }
                catch
                {
                    log.CompletionStatus = unavailable;
                }
            }
            else
            {
                message = new StringBuilder("HeaderElements ");
            }

            XElement detailsElement = xmlJobInfo.Descendants("JobLogDetails").FirstOrDefault();

            if (detailsElement != null)
            {
                try
                {
                    log.FileType = detailsElement.Element("cFileType").Value;
                }
                catch
                {
                    log.FileType = unavailable;
                }
                try
                {
                    log.ScannedPages = short.Parse(detailsElement.Element("cPagesScanned").Value);
                }
                catch { } //Just move on
                try
                {
                    log.DssVersion = detailsElement.Element("cDssSoftwareVersionColon").Value;
                }
                catch
                {
                    log.DssVersion = unavailable;
                }
                try
                {
                    log.ProcessedBy = detailsElement.Element("cJobProcessedBy").Value;
                }
                catch
                {
                    log.ProcessedBy = unavailable;
                }
            }
            else
            {
                if (message == null)
                {
                    message = new StringBuilder();
                }
                message.Append("JobLogDetails ");
            }

            //Check to see if any data was not logged because of null elements
            if (message != null)
            {
                message.Append("elements not found.");
                message.Append(xmlJobInfo.ToString());
                TraceFactory.Logger.Debug(message.ToString());
            }
        }
        /// <summary>
        /// Gets the data for each job ID and logs it
        /// </summary>
        private void LogData(SqlAdapter adapter, DSSJobLogTable logTable, string sqlText, ref List <Guid> jobIds)
        {
            TraceFactory.Logger.Debug("Processing {0}.".FormatWith(logTable.ToString()));

            DbDataReader jobsReader = null;

            Retry.WhileThrowing(() =>
            {
                jobsReader = adapter.ExecuteReader(sqlText);
            }, 3, TimeSpan.FromSeconds(5), _retryExceptions);

            try
            {
                bool continueRead = true;
                while (continueRead)
                {
                    try
                    {
                        continueRead = jobsReader.Read();
                    }
                    catch (SqlException sqlEx)
                    {
                        if (sqlEx.Number == 1205)
                        {
                            TraceFactory.Logger.Debug("Deadlock encountered.  Continue to process.");
                        }
                        else
                        {
                            TraceFactory.Logger.Error(sqlEx); //Unexpected SQL exception.
                        }
                        continue;                             //Skip to the next record
                    }

                    if (continueRead)
                    {
                        // Successful read. Log the entry
                        DigitalSendServerJobLogger log = null;
                        switch (logTable)
                        {
                        case DSSJobLogTable.JobLogDevice:
                            log = CreateLogFromDevice(jobsReader);
                            break;

                        case DSSJobLogTable.JobLogMaster:
                            log = CreateLogFromMaster(jobsReader);
                            break;
                        }

                        SubmitLog(log);
                        TraceFactory.Logger.Debug("Found job with ID " + log.DigitalSendJobId.ToString());
                        //Job successfully logged.  Remove it from the list
                        jobIds.Remove(log.DigitalSendJobId);

                        // See if we've found a new session ID
                        if (log.SessionId != _sessionId)
                        {
                            _sessionId = log.SessionId;
                            if (RetrievedSession != null)
                            {
                                RetrievedSession(this, new SessionEventArgs(log.SessionId));
                            }
                        }
                    }
                }
            }
            finally
            {
                if (jobsReader != null)
                {
                    jobsReader.Close();
                    jobsReader.Dispose();
                }
            }
        }
        /// <summary>
        /// Retrieves the job records from the DSS database.
        /// For the records that are not found in the database, the following may be logged to the data logger:
        /// unavailable - The job ID was found in the DSS database, and partial data was retrieved for the job.  Represents job data that could not be retrieved.
        /// unknown - While attempting to find the job ID in the database, a database error occurred, and after retrying the operation several times, processing moved on.
        /// missing - The job was not found in either the Master or the Device table.
        /// </summary>
        public void RetrieveRecords()
        {
            TraceFactory.Logger.Debug("Retrieving Job IDs from STFTransactionLog table.");
            using (SqlAdapter adapter = new SqlAdapter(_connectionString))
            {
                List <Guid> jobIds = new List <Guid>(); //List of records that have been updated

                try
                {
                    using (DbDataReader idsReader = adapter.ExecuteReader(Resource.TransactionRetrieveSql))
                    {
                        while (idsReader.Read())
                        {
                            jobIds.Add((Guid)idsReader["JobId"]);
                        }
                        idsReader.Close();
                    }
                }
                catch (SqlException sqlEx)
                {
                    TraceFactory.Logger.Error("Error retrieving list of Job IDs.  Aborting.", sqlEx);
                    return;
                }

                TraceFactory.Logger.Debug("Found {0} records.".FormatWith(jobIds.Count));

                if (jobIds.Any())
                {
                    // Capture list of job IDs now while we have the entire list, to be used to cleanup the transaction table later.
                    string jobIdList    = "'" + string.Join("','", jobIds) + "'";
                    string notFoundText = "<missing>";

                    try
                    {
                        //Log data from JobLogMaster.
                        LogData(adapter, DSSJobLogTable.JobLogMaster, Resource.JobLogMasterSql.FormatWith(jobIdList), ref jobIds);
                    }
                    catch (SqlException sqlEx)
                    {
                        TraceFactory.Logger.Error("Unable to process JobLogMaster.", sqlEx);
                        notFoundText = "<unknown>";
                    }

                    //Check to see if there were any jobs that weren't processed
                    if (jobIds.Any())
                    {
                        // Recreate list of job IDs, since some of the jobs have already been processed and removed from jobIds
                        string unprocessedJobIdList = "'" + string.Join("','", jobIds) + "'";

                        try
                        {
                            //Log data from JobLogDevice.
                            LogData(adapter, DSSJobLogTable.JobLogDevice, Resource.JobLogDeviceSql.FormatWith(unprocessedJobIdList), ref jobIds);
                        }
                        catch (SqlException sqlEx)
                        {
                            TraceFactory.Logger.Error("Unable to process JobLogDevice.", sqlEx);
                            notFoundText = "<unknown>";
                        }
                    }

                    // Any job Ids left over at this point were not found in either the master nor the device tables
                    // Log an entry in the data log to flag data that was not found.
                    foreach (Guid jobId in jobIds)
                    {
                        DigitalSendServerJobLogger log = CreateLogForNotFound(jobId, notFoundText);
                        SubmitLog(log);
                    }

                    //At this point, all jobs Ids have been processed
                    jobIds.Clear();

                    //Remove the JobIds from the transaction table.
                    string sql = Resource.TransactionCleanupSql.FormatWith(jobIdList);
                    //TraceFactory.Logger.Debug(sql);
                    Retry.WhileThrowing(() =>
                    {
                        adapter.ExecuteNonQuery(sql);
                    }, 5, TimeSpan.FromSeconds(5), _retryExceptions);
                }
            }
        }