Skip to content
Snippets Groups Projects
Commit 4fd69398 authored by Niko Ehrenfeuchter's avatar Niko Ehrenfeuchter :keyboard:
Browse files

Create class library for XML wrappers.

Note that this only adds the new class library, it does not yet adjust
the existing service code to use it!

Refers to #21
parent dc935361
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{166D65D5-EE10-4364-8AA3-4D86BA5CE244}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ATXSerializables</RootNamespace>
<AssemblyName>ATXSerializables</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DriveToCheck.cs" />
<Compile Include="ServiceConfig.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceStatus.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
using System.Xml.Serialization;
namespace ATXSerializables
{
/// <summary>
/// Helper class for the nested SpaceMonitoring sections.
/// </summary>
public class DriveToCheck
{
[XmlElement("DriveName")]
public string DriveName { get; set; }
// the value is to be compared to System.IO.DriveInfo.TotalFreeSpace
// hence we use the same type (long) to avoid unnecessary casts later:
[XmlElement("SpaceThreshold")]
public long SpaceThreshold { get; set; }
}
}
\ No newline at end of file
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ATXSerializables")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Universitaet Basel")]
[assembly: AssemblyProduct("ATXSerializables")]
[assembly: AssemblyCopyright("Copyright © Universitaet Basel 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c1ed2607-6acb-4202-9354-65d19c5a06d1")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;
namespace ATXSerializables
{
/// <summary>
/// configuration class based on xml
/// </summary>
[Serializable]
public class ServiceConfig
{
[XmlIgnore] public string ValidationWarnings;
public ServiceConfig() {
ValidationWarnings = "";
// set values for the optional XML elements:
SmtpHost = "";
SmtpPort = 25;
SmtpUserCredential = "";
SmtpPasswortCredential = "";
EmailPrefix = "";
AdminEmailAdress = "";
AdminDebugEmailAdress = "";
GraceNotificationDelta = 720;
InterPacketGap = 0;
EnforceInheritedACLs = true;
}
#region required configuration parameters
/// <summary>
/// A human friendly name for the host, to be used in emails etc.
/// </summary>
public string HostAlias { get; set; }
/// <summary>
/// A human friendly name for the target, to be used in emails etc.
/// </summary>
public string DestinationAlias { get; set; }
/// <summary>
/// The base drive for the spooling directories (incoming and managed).
/// </summary>
public string SourceDrive { get; set; }
/// <summary>
/// The name of a directory on SourceDrive that is monitored for new files.
/// </summary>
public string IncomingDirectory { get; set; }
/// <summary>
/// The name of a marker file to be placed in all **sub**directories
/// inside the IncomingDirectory.
/// </summary>
public string MarkerFile { get; set; }
/// <summary>
/// A directory on SourceDrive to hold the three subdirectories "DONE",
/// "PROCESSING" and "UNMATCHED" used during and after transfers.
/// </summary>
public string ManagedDirectory { get; set; }
/// <summary>
/// Target path to transfer files to. Usually a UNC location.
/// </summary>
public string DestinationDirectory { get; set; }
/// <summary>
/// The name of a subdirectory in the DestinationDirectory to be used
/// to keep the temporary data of running transfers.
/// </summary>
public string TmpTransferDir { get; set; }
public string EmailFrom { get; set; }
public int ServiceTimer { get; set; }
public int MaxCpuUsage { get; set; }
public int MinAvailableMemory { get; set; }
public int AdminNotificationDelta { get; set; }
public int StorageNotificationDelta { get; set; }
/// <summary>
/// GracePeriod: number of days after data in the "DONE" location expires,
/// which will trigger a summary email to the admin address.
/// </summary>
public int GracePeriod { get; set; }
public bool SendAdminNotification { get; set; }
public bool SendTransferNotification { get; set; }
public bool Debug { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "DriveToCheck")]
public List<DriveToCheck> SpaceMonitoring { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "ProcessName")]
public List<string> BlacklistedProcesses { get; set; }
#endregion
#region optional configuration parameters
public string SmtpHost { get; set; }
public string SmtpUserCredential { get; set; }
public string SmtpPasswortCredential { get; set; }
public int SmtpPort { get; set; }
public string EmailPrefix { get; set; }
public string AdminEmailAdress { get; set; }
public string AdminDebugEmailAdress { get; set; }
public int GraceNotificationDelta { get; set; }
public int InterPacketGap { get; set; }
/// <summary>
/// EnforceInheritedACLs: whether to enforce ACL inheritance when moving files and
/// directories, see https://support.microsoft.com/en-us/help/320246 for more details.
/// </summary>
public bool EnforceInheritedACLs { get; set; }
#endregion
public static void Serialize(string file, ServiceConfig c) {
// the config is never meant to be written by us, therefore:
throw new SettingsPropertyIsReadOnlyException("The config file must not be written by the service!");
}
public static ServiceConfig Deserialize(string file) {
var xs = new XmlSerializer(typeof(ServiceConfig));
var reader = File.OpenText(file);
var config = (ServiceConfig) xs.Deserialize(reader);
reader.Close();
ValidateConfiguration(config);
return config;
}
private static void ValidateConfiguration(ServiceConfig c) {
if (string.IsNullOrEmpty(c.SourceDrive) ||
string.IsNullOrEmpty(c.IncomingDirectory) ||
string.IsNullOrEmpty(c.ManagedDirectory))
throw new ConfigurationErrorsException("mandatory parameter missing!");
if (c.SourceDrive.Substring(1) != @":\")
throw new ConfigurationErrorsException("SourceDrive must be a drive " +
@"letter followed by a colon and a backslash, e.g. 'D:\'!");
// make sure SourceDrive is a local (fixed) disk:
var driveInfo = new DriveInfo(c.SourceDrive);
if (driveInfo.DriveType != DriveType.Fixed)
throw new ConfigurationErrorsException("SourceDrive (" + c.SourceDrive +
") must be a local (fixed) drive, OS reports '" +
driveInfo.DriveType + "')!");
// spooling directories: IncomingDirectory + ManagedDirectory
if (c.IncomingDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("IncomingDirectory must not start with a backslash!");
if (c.ManagedDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("ManagedDirectory must not start with a backslash!");
if (!Directory.Exists(c.DestinationDirectory))
throw new ConfigurationErrorsException("can't find destination: " + c.DestinationDirectory);
var tmpTransferPath = Path.Combine(c.DestinationDirectory, c.TmpTransferDir);
if (!Directory.Exists(tmpTransferPath))
throw new ConfigurationErrorsException("temporary transfer dir doesn't exist: " + tmpTransferPath);
if (c.ServiceTimer < 1000)
throw new ConfigurationErrorsException("ServiceTimer must not be smaller than 1000 ms!");
// NON-CRITICAL stuff just adds messages to ValidationWarnings:
// DestinationDirectory
if (!c.DestinationDirectory.StartsWith(@"\\"))
c.ValidationWarnings += " - <DestinationDirectory> is not a UNC path!\n";
}
public string Summary() {
var msg =
"HostAlias: " + HostAlias + "\n" +
"SourceDrive: " + SourceDrive + "\n" +
"IncomingDirectory: " + IncomingDirectory + "\n" +
"MarkerFile: " + MarkerFile + "\n" +
"ManagedDirectory: " + ManagedDirectory + "\n" +
"GracePeriod: " + GracePeriod + "\n" +
"DestinationDirectory: " + DestinationDirectory + "\n" +
"TmpTransferDir: " + TmpTransferDir + "\n" +
"EnforceInheritedACLs: " + EnforceInheritedACLs + "\n" +
"ServiceTimer: " + ServiceTimer + "\n" +
"InterPacketGap: " + InterPacketGap + "\n" +
"MaxCpuUsage: " + MaxCpuUsage + "\n" +
"MinAvailableMemory: " + MinAvailableMemory + "\n";
foreach (var processName in BlacklistedProcesses) {
msg += "BlacklistedProcess: " + processName + "\n";
}
foreach (var driveToCheck in SpaceMonitoring) {
msg += "Drive to check free space: " + driveToCheck.DriveName +
" (threshold: " + driveToCheck.SpaceThreshold + ")" + "\n";
}
if (string.IsNullOrEmpty(SmtpHost)) {
msg += "SmtpHost: ====== Not configured, disabling email! ======" + "\n";
} else {
msg +=
"SmtpHost: " + SmtpHost + "\n" +
"EmailFrom: " + EmailFrom + "\n" +
"AdminEmailAdress: " + AdminEmailAdress + "\n" +
"AdminDebugEmailAdress: " + AdminDebugEmailAdress + "\n" +
"StorageNotificationDelta: " + StorageNotificationDelta + "\n" +
"AdminNotificationDelta: " + AdminNotificationDelta + "\n" +
"GraceNotificationDelta: " + GraceNotificationDelta + "\n";
}
return msg;
}
}
}
using System;
using System.IO;
using System.Xml.Serialization;
namespace ATXSerializables
{
[Serializable]
public class ServiceStatus
{
[XmlIgnore] string _storageFile; // remember where we came from
[XmlIgnore] private ServiceConfig _config;
[XmlIgnore] public string ValidationWarnings;
private DateTime _lastStatusUpdate;
private DateTime _lastStorageNotification;
private DateTime _lastAdminNotification;
private DateTime _lastGraceNotification;
private string _limitReason;
string _currentTransferSrc;
string _currentTargetTmp;
bool _transferInProgress;
private bool _serviceSuspended;
private bool _cleanShutdown;
private long _currentTransferSize;
[XmlElement("LastStatusUpdate", DataType = "dateTime")]
public DateTime LastStatusUpdate {
get { return _lastStatusUpdate; }
set { _lastStatusUpdate = value; }
}
[XmlElement("LastStorageNotification", DataType = "dateTime")]
public DateTime LastStorageNotification {
get { return _lastStorageNotification; }
set {
_lastStorageNotification = value;
Serialize();
}
}
[XmlElement("LastAdminNotification", DataType = "dateTime")]
public DateTime LastAdminNotification {
get { return _lastAdminNotification; }
set {
_lastAdminNotification = value;
Serialize();
}
}
[XmlElement("LastGraceNotification", DataType = "dateTime")]
public DateTime LastGraceNotification {
get { return _lastGraceNotification; }
set {
_lastGraceNotification = value;
Serialize();
}
}
public string LimitReason {
get { return _limitReason; }
set {
_limitReason = value;
// log("LimitReason was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public string CurrentTransferSrc {
get { return _currentTransferSrc; }
set {
_currentTransferSrc = value;
// log("CurrentTransferSrc was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public string CurrentTargetTmp {
get { return _currentTargetTmp; }
set {
_currentTargetTmp = value;
// log("CurrentTargetTmp was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public bool ServiceSuspended {
get { return _serviceSuspended; }
set {
_serviceSuspended = value;
// log("ServiceSuspended was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public bool TransferInProgress {
get { return _transferInProgress; }
set {
_transferInProgress = value;
// log("FilecopyFinished was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
/// <summary>
/// Indicates whether the service was cleanly shut down (false while the service is running).
/// </summary>
public bool CleanShutdown {
get { return _cleanShutdown; }
set {
_cleanShutdown = value;
Serialize();
}
}
public long CurrentTransferSize {
get { return _currentTransferSize; }
set {
_currentTransferSize = value;
// log("CurrentTransferSize was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public ServiceStatus() {
_currentTransferSrc = "";
_currentTargetTmp = "";
_transferInProgress = false;
}
public void Serialize() {
/* During de-serialization, the setter methods get called as well but
* we should not serialize until the deserialization has completed.
* As the storage file name will only be set after this, it is sufficient
* to test for this (plus, we can't serialize anyway without it).
*/
if (_storageFile == null) {
// log("File name for XML serialization is not set, doing nothing.");
return;
}
// update the timestamp:
LastStatusUpdate = DateTime.Now;
try {
var xs = new XmlSerializer(GetType());
var writer = File.CreateText(_storageFile);
xs.Serialize(writer, this);
writer.Flush();
writer.Close();
}
catch (Exception ex) {
log("Error in Serialize(): " + ex.Message);
}
log("Finished serializing " + _storageFile);
}
static void log(string message) {
// use Console.WriteLine until proper logging is there (running as a system
// service means those messages will disappear):
Console.WriteLine(message);
/*
using (var sw = File.AppendText(@"C:\Tools\AutoTx\console.log")) {
sw.WriteLine(message);
}
*/
}
public static ServiceStatus Deserialize(string file, ServiceConfig config) {
ServiceStatus status;
var xs = new XmlSerializer(typeof(ServiceStatus));
try {
var reader = File.OpenText(file);
status = (ServiceStatus) xs.Deserialize(reader);
reader.Close();
}
catch (Exception) {
// if reading the status XML fails, we return an empty (new) one
status = new ServiceStatus();
}
status._config = config;
ValidateStatus(status);
// now set the storage filename:
status._storageFile = file;
return status;
}
private static void ValidateStatus(ServiceStatus s) {
// CurrentTransferSrc
if (s.CurrentTransferSrc.Length > 0
&& !Directory.Exists(s.CurrentTransferSrc)) {
s.ValidationWarnings += " - found non-existing source path of an unfinished " +
"transfer: " + s.CurrentTransferSrc + "\n";
s.CurrentTransferSrc = "";
}
// CurrentTargetTmp
var currentTargetTmpPath = Path.Combine(s._config.DestinationDirectory,
s._config.TmpTransferDir,
s.CurrentTargetTmp);
if (s.CurrentTargetTmp.Length > 0
&& !Directory.Exists(currentTargetTmpPath)) {
s.ValidationWarnings += " - found non-existing temporary path of an " +
"unfinished transfer: " + currentTargetTmpPath + "\n";
s.CurrentTargetTmp = "";
}
}
public string Summary() {
return
"CurrentTransferSrc: " + CurrentTransferSrc + "\n" +
"CurrentTargetTmp: " + CurrentTargetTmp + "\n" +
"TransferInProgress: " + TransferInProgress + "\n" +
"CurrentTransferSize: " + CurrentTransferSize + "\n" +
"LastStatusUpdate: " +
LastStatusUpdate.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastStorageNotification: " +
LastStorageNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastAdminNotification: " +
LastAdminNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastGraceNotification: " +
LastGraceNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n";
}
}
}
......@@ -5,6 +5,8 @@ VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ATXProject", "AutoTx\ATXProject.csproj", "{5CB67E3A-E63A-4791-B90B-8CEF0027AEAD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ATXSerializables", "ATXSerializables\ATXSerializables.csproj", "{166D65D5-EE10-4364-8AA3-4D86BA5CE244}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -15,6 +17,10 @@ Global
{5CB67E3A-E63A-4791-B90B-8CEF0027AEAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CB67E3A-E63A-4791-B90B-8CEF0027AEAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CB67E3A-E63A-4791-B90B-8CEF0027AEAD}.Release|Any CPU.Build.0 = Release|Any CPU
{166D65D5-EE10-4364-8AA3-4D86BA5CE244}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{166D65D5-EE10-4364-8AA3-4D86BA5CE244}.Debug|Any CPU.Build.0 = Debug|Any CPU
{166D65D5-EE10-4364-8AA3-4D86BA5CE244}.Release|Any CPU.ActiveCfg = Release|Any CPU
{166D65D5-EE10-4364-8AA3-4D86BA5CE244}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment