Skip to content

Commit 11bc0fa

Browse files
committed
Complete .NET 10 cross-platform migration updates
1 parent 47bfb64 commit 11bc0fa

18 files changed

Lines changed: 482 additions & 305 deletions

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
build:
11+
strategy:
12+
matrix:
13+
os: [windows-latest, ubuntu-latest, macos-latest]
14+
runs-on: ${{ matrix.os }}
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup .NET 10
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: '10.0.x'
23+
24+
- name: Restore
25+
run: dotnet restore ServMon.sln
26+
27+
- name: Build
28+
run: dotnet build ServMon.sln -c Release --no-restore
29+
30+
- name: Test
31+
run: dotnet test ServMon.sln -c Release --no-build --verbosity normal
32+
33+
- name: Publish Console
34+
run: dotnet publish Console/ServMon/ServMon.csproj -c Release --no-build -o artifacts/console
35+
36+
- name: Publish WebApp
37+
run: dotnet publish WebApp/ServMonWeb.csproj -c Release --no-build -o artifacts/webapp
38+
39+
- name: Upload artifacts
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: servmon-${{ matrix.os }}
43+
path: artifacts/

Console/ServMon/FtpService.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Net;
65
using System.Text;
76
using System.Threading.Tasks;
7+
using FluentFTP;
88
using WCMS.Common.Utilities;
99

1010
namespace ServMon
@@ -18,21 +18,25 @@ public override ServResponse Execute()
1818
var callSuccess = false;
1919
var text = string.Empty;
2020
var response = new ServResponse();
21-
var path = FtpHelper.UrlEncode(Url);
2221
try
2322
{
24-
var client = (FtpWebRequest)WebRequest.Create(path);
25-
client.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
26-
client.Proxy = WebRequest.GetSystemWebProxy();
27-
client.Credentials = new NetworkCredential(Username, Password);
28-
using (var res = client.GetResponse() as FtpWebResponse)
23+
var uri = new Uri(Url);
24+
var host = uri.Host;
25+
var remotePath = Uri.UnescapeDataString(uri.AbsolutePath);
26+
27+
using var client = new FtpClient(host);
28+
if (!string.IsNullOrEmpty(Username))
29+
client.Credentials = new System.Net.NetworkCredential(Username, Password);
30+
client.Connect();
31+
32+
var listing = client.GetListing(remotePath);
33+
var sb = new StringBuilder();
34+
foreach (var item in listing)
2935
{
30-
var reader = new StreamReader(res.GetResponseStream(), System.Text.Encoding.ASCII);
31-
text = reader.ReadToEnd();
32-
reader.Close();
33-
res.Close();
34-
callSuccess = true;
36+
sb.AppendLine(item.FullName);
3537
}
38+
text = sb.ToString();
39+
callSuccess = true;
3640
}
3741
catch (Exception ex)
3842
{

Console/ServMon/HttpService.cs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,36 @@
33
using System.IO;
44
using System.Linq;
55
using System.Net;
6+
using System.Net.Http;
67
using System.Text;
78
using System.Threading.Tasks;
89

910
namespace ServMon
1011
{
1112
class HttpService : CommonService
1213
{
14+
private static readonly HttpClient _httpClient;
15+
16+
static HttpService()
17+
{
18+
var handler = new HttpClientHandler
19+
{
20+
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
21+
};
22+
_httpClient = new HttpClient(handler);
23+
}
24+
1325
public override ServResponse Execute()
1426
{
1527
PreExecute();
1628

17-
// Ignore invalid SSL certificates
18-
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
19-
2029
var callSuccess = false;
2130
var text = string.Empty;
2231
var response = new ServResponse();
2332
try
2433
{
25-
var req = WebRequest.Create(Url);
26-
var res = req.GetResponse();
27-
var data = res.GetResponseStream();
28-
using (var sr = new StreamReader(data))
29-
{
30-
text = sr.ReadToEnd();
31-
}
32-
res.Close();
34+
using var httpResponse = _httpClient.GetAsync(Url).GetAwaiter().GetResult();
35+
text = httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
3336
callSuccess = true;
3437
}
3538
catch (Exception ex)

Console/ServMon/Program.cs

Lines changed: 104 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -12,117 +12,119 @@ namespace ServMon
1212
class Program
1313
{
1414
/// <summary>
15-
/// Sleep interval in minutes.
15+
/// Sleep interval in seconds.
1616
/// </summary>
1717
private static int execInterval;
18-
private static bool writeState = true;
18+
private static volatile bool writeState = true;
1919

20-
static void Main(string[] args)
20+
static async Task Main(string[] args)
2121
{
22-
var workerThreads = new List<Thread>();
22+
using var cts = new CancellationTokenSource();
23+
var token = cts.Token;
2324

24-
Console.WriteLine("ServMon started. Press ENTER to stop.");
25+
Console.CancelKeyPress += (_, e) =>
26+
{
27+
e.Cancel = true;
28+
cts.Cancel();
29+
};
30+
31+
Console.WriteLine("ServMon started. Press Ctrl+C to stop.");
2532
Console.WriteLine();
2633

27-
var schedulerThread = new Thread(() =>
34+
try
2835
{
29-
try
30-
{
31-
ServManager.Instance.ReadConfig();
32-
}
33-
catch (Exception ex)
34-
{
35-
LogHelper.WriteLog(true, ex);
36-
return;
37-
}
36+
ServManager.Instance.ReadConfig();
37+
}
38+
catch (Exception ex)
39+
{
40+
LogHelper.WriteLog(true, ex);
41+
return;
42+
}
3843

39-
execInterval = ServManager.Instance.Interval;
44+
execInterval = ServManager.Instance.Interval;
45+
46+
Console.WriteLine("Default exec interval: {0} seconds", execInterval);
47+
Console.WriteLine("Begin instrumentation...");
48+
Console.WriteLine();
4049

41-
Console.WriteLine("Default exec interval: {0} seconds", execInterval);
42-
Console.WriteLine("Begin instrumentation...");
43-
Console.WriteLine();
50+
var items = ServManager.Instance.Items;
51+
var workerTasks = new List<Task>();
4452

45-
var items = ServManager.Instance.Items;
46-
foreach (var item in items)
53+
foreach (var item in items)
54+
{
55+
if (item.Value.Enabled)
4756
{
48-
if (item.Value.Enabled)
57+
var serv = item.Value;
58+
var task = Task.Run(async () =>
4959
{
50-
var workerThread = new Thread(() =>
60+
bool smsSent = false;
61+
while (!token.IsCancellationRequested)
5162
{
52-
bool smsSent = false; // SMS won't be sent successively, will always skip one instance for consecutive failures.
53-
while (true)
54-
{
55-
var serv = item.Value;
56-
//Console.WriteLine();
57-
Console.WriteLine("{0}: Started", serv.Name);
63+
Console.WriteLine("{0}: Started", serv.Name);
5864

59-
var success = serv.Success;
60-
var response = serv.Execute();
61-
if (!response.Success)
65+
var success = serv.Success;
66+
var response = serv.Execute();
67+
if (!response.Success)
68+
{
69+
if (!string.IsNullOrEmpty(response.Message))
70+
Console.WriteLine("{0}: FAILED. {1}", serv.Name, response.Message);
71+
else
72+
Console.WriteLine("{0}: FAILED.", serv.Name);
73+
var mail = new MailSender();
74+
mail.Subject = string.Format("ServMon: {0} - Check FAILED", serv.Name);
75+
mail.Message = string.Format("Service: {0}<br/>Time: {1}<br/>Error: {2}<br/><br/>Trace: {3}", serv.Name, serv.LastUpdate, response.Message, response.StackTrace);
76+
mail.To = (ServManager.Instance.MailSettings.To + "," + serv.ToEmails).Trim().TrimEnd(',');
77+
mail.Send();
78+
Console.WriteLine("{0}: Email sent to {1}", serv.Name, mail.To);
79+
80+
var smsTo = (ServManager.Instance.SmsSettings.To + "," + serv.ToNumbers).Trim().TrimEnd(',');
81+
if (ServManager.Instance.SmsSettings.Enabled && serv.EnableSms && !string.IsNullOrEmpty(smsTo))
6282
{
63-
if (!string.IsNullOrEmpty(response.Message))
64-
Console.WriteLine("{0}: FAILED. {1}", serv.Name, response.Message);
83+
if (!smsSent)
84+
{
85+
var sms = new SmsSender();
86+
sms.To = smsTo;
87+
sms.Message = string.Format("{0} - FAILED, pls chk. %0AError: {1}", serv.Name, response.Message);
88+
sms.Send();
89+
90+
Console.WriteLine("{0}: SMS sent to {1}", serv.Name, smsTo);
91+
smsSent = true;
92+
}
6593
else
66-
Console.WriteLine("{0}: FAILED.", serv.Name);
67-
var mail = new MailSender();
68-
mail.Subject = string.Format("ServMon: {0} - Check FAILED", serv.Name);
69-
mail.Message = string.Format("Service: {0}<br/>Time: {1}<br/>Error: {2}<br/><br/>Trace: {3}", serv.Name, serv.LastUpdate, response.Message, response.StackTrace);
70-
mail.To = (ServManager.Instance.MailSettings.To + "," + serv.ToEmails).Trim().TrimEnd(',');
71-
mail.Send();
72-
Console.WriteLine("{0}: Email sent to {1}", serv.Name, mail.To);
73-
74-
var smsTo = (ServManager.Instance.SmsSettings.To + "," + serv.ToNumbers).Trim().TrimEnd(',');
75-
if (ServManager.Instance.SmsSettings.Enabled && serv.EnableSms && !string.IsNullOrEmpty(smsTo))
7694
{
77-
if (!smsSent)
78-
{
79-
var sms = new SmsSender();
80-
sms.To = smsTo;
81-
sms.Message = string.Format("{0} - FAILED, pls chk. %0AError: {1}", serv.Name, response.Message);
82-
sms.Send();
83-
84-
Console.WriteLine("{0}: SMS sent to {1}", serv.Name, smsTo);
85-
smsSent = true;
86-
}
87-
else
88-
{
89-
smsSent = false;
90-
}
95+
smsSent = false;
9196
}
9297
}
93-
else
94-
{
95-
Console.WriteLine("{0}: Success!", serv.Name);
96-
smsSent = false;
97-
}
98-
99-
if (success != response.Success)
100-
{
101-
// Change in status, write state to json file
102-
writeState = true;
103-
}
98+
}
99+
else
100+
{
101+
Console.WriteLine("{0}: Success!", serv.Name);
102+
smsSent = false;
103+
}
104104

105-
//Console.WriteLine("{0}: Sleeping...", serv.Name);
106-
Sleep(serv.Interval);
105+
if (success != response.Success)
106+
{
107+
writeState = true;
107108
}
108-
});
109109

110-
workerThreads.Add(workerThread);
110+
await SleepAsync(serv.Interval, token);
111+
}
112+
}, token);
111113

112-
workerThread.IsBackground = false; // Always dependent to job threads // !forceExecute;
113-
workerThread.SetApartmentState(ApartmentState.STA);
114-
workerThread.Start();
115-
}
114+
workerTasks.Add(task);
116115
}
116+
}
117117

118+
// State writer task
119+
var stateWriterTask = Task.Run(async () =>
120+
{
118121
var jsonCheck = 0;
119-
while (true)
122+
while (!token.IsCancellationRequested)
120123
{
121124
jsonCheck += 10;
122-
Sleep(10); // Check status changes every 10 seconds
125+
await SleepAsync(10, token);
123126
if (writeState || jsonCheck > execInterval)
124127
{
125-
// Build Json object
126128
var json = new JObject(
127129
new JProperty("services",
128130
new JArray(
@@ -144,26 +146,33 @@ from i in items.Values
144146
jsonCheck = 0;
145147
}
146148
}
147-
});
148-
schedulerThread.IsBackground = false; // Always dependent to job threads // !forceExecute;
149-
schedulerThread.SetApartmentState(ApartmentState.STA);
150-
schedulerThread.Start();
151-
Console.ReadLine();
152-
153-
Console.WriteLine("Aborting Threads. Press wait...");
154-
// Abort Scheduler Thread
155-
schedulerThread.Abort();
156-
foreach (var thread in workerThreads)
157-
thread.Abort();
149+
}, token);
150+
151+
workerTasks.Add(stateWriterTask);
152+
153+
try
154+
{
155+
await Task.WhenAll(workerTasks);
156+
}
157+
catch (OperationCanceledException)
158+
{
159+
// Expected on shutdown
160+
}
158161

159162
Console.WriteLine();
160-
Console.WriteLine("Processing stopped. Press ENTER to exit.");
161-
Console.ReadLine();
163+
Console.WriteLine("Processing stopped gracefully.");
162164
}
163165

164-
private static void Sleep(int interval = -1)
166+
private static async Task SleepAsync(int interval, CancellationToken token)
165167
{
166-
Thread.Sleep((interval > 0 ? interval : execInterval) * 1000);
168+
try
169+
{
170+
await Task.Delay((interval > 0 ? interval : execInterval) * 1000, token);
171+
}
172+
catch (OperationCanceledException)
173+
{
174+
// Expected on cancellation
175+
}
167176
}
168177
}
169178
}

0 commit comments

Comments
 (0)