-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathPlantUmlProcessManager.cs
212 lines (182 loc) · 7.96 KB
/
PlantUmlProcessManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace MarkSpecs
{
/// <summary>
/// Manage the PlantUml processes.
/// It is possible to launch several PlantUml processes.
/// </summary>
public class PlantUmlProcessManager : IDisposable
{
//Record the list of the process to kill them on request.
private List<Process> Processes = new List<Process>();
private string javaPath;
private int instanceNb;
private string plantUmlPath;
private string plantUmlParameters;
private int initPort;
private bool disposedValue;
public static bool IsReadyToLaunch()
{
var plantumlpath = ConfigurationManager.AppSettings["PlantUml.Path"];
var plantumlInstance = int.Parse(ConfigurationManager.AppSettings["PlantUml.InstancesCount"]);
if ((plantumlInstance > 0) && (!String.IsNullOrWhiteSpace(plantumlpath)))
{
if (!File.Exists(plantumlpath))
{
//try to launch the local version of plantuml
var localPlantUmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, plantumlpath);
if (!File.Exists(localPlantUmlPath))
return false;
}
return true;
}
return false;
}
//The constructor also launches the process.
public PlantUmlProcessManager()
{
RetrieveConfiguration();
if (!File.Exists(javaPath))
throw new ApplicationException("Java.exe not found: " + javaPath);
if (!File.Exists(plantUmlPath))
throw new ApplicationException("plantuml.jar not found in " + plantUmlPath);
//Each PlantUml will have its own thread.
//WaitHandles is used to wait about all threads.
WaitHandle[] waitHandles = new WaitHandle[instanceNb];
//Build the required number of processes.
//Then launch the threads.
///<see cref="https://stackoverflow.com/a/29753402/12731908"/> for the ConsoleOutputs.
///<see cref="https://stackoverflow.com/a/4190972/12731908"/> for the threads.
for (int i = 0; i < instanceNb; i++)
{
var argument = "-jar " + plantUmlPath + " -ftp:" + (initPort + i) + " -tsvg " + plantUmlParameters;
argument = argument.Trim();
ProcessStartInfo pInfo = new ProcessStartInfo(javaPath, argument)
{
//CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
var id = i; //a way to have an id of the process, only for console print.
var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
var thread = new Thread(() =>
{
Process process = new Process
{
StartInfo = pInfo
};
Console.WriteLine("Process " + id + " " + argument);
process.Start();
handle.Set();
//Redirect the outputs to the Console.
process.OutputDataReceived += (s, e) => Console.WriteLine("[" + id + "]" + e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine("[" + id + "]" + e.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
Processes.Add(process);
process.WaitForExit();
});
waitHandles[i] = handle;
thread.Start();
}
WaitHandle.WaitAll(waitHandles);
}
/// <summary>
/// Retrieve the configuration from the App.config file.
/// </summary>
private void RetrieveConfiguration()
{
//Get the Java path
javaPath = ConfigurationManager.AppSettings["JavaPath"];
if (string.IsNullOrEmpty(javaPath.Trim()))
javaPath = RetrieveJavaPathFromEnvironmentVariable();
instanceNb = int.Parse(ConfigurationManager.AppSettings["PlantUml.InstancesCount"]);
initPort = int.Parse(ConfigurationManager.AppSettings["PlantUml.Port"]);
plantUmlPath = ConfigurationManager.AppSettings["PlantUml.Path"];
plantUmlParameters = ConfigurationManager.AppSettings["PlantUml.Parameter"];
}
/// <summary>
/// Retrieve Java from the environment variables.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentNullException">Java environment not identified.</exception>
private static string RetrieveJavaPathFromEnvironmentVariable()
{
//Search in user variables first
var paths = Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.User).Split(";".ToCharArray());
#nullable enable
var javaPath = SearchJavaInPaths(paths);
if (javaPath is null)
{//else search on the machine
paths = Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.Machine).Split(";".ToCharArray());
javaPath = SearchJavaInPaths(paths);
}
return javaPath ?? throw new ArgumentNullException("Unable to identify Java environment");
}
/// <summary>
/// Search Java path in a string.
/// </summary>
/// <param name="paths"></param>
/// <returns></returns>
private static string? SearchJavaInPaths(string[] paths)
{
string? applicableJavaPath = null;
Version? applicableJavaVersion = null;
foreach (var pth in paths)
{
if (pth.Contains(@"\Java\"))
{
if (File.Exists(Path.Combine(pth, "java.exe")))
{
var versionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(pth, "java.exe"));
Version currentVersion = new Version(versionInfo.ProductVersion.Replace(",", "."));
if (applicableJavaVersion is null)
{
applicableJavaVersion = currentVersion;
applicableJavaPath = Path.Combine(pth, "java.exe");
}
else if (currentVersion > applicableJavaVersion)
{
applicableJavaVersion = currentVersion;
applicableJavaPath = Path.Combine(pth, "java.exe");
}
}
}
}
return applicableJavaPath;
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
foreach (var process in this.Processes)
{
process.Kill();
}
}
disposedValue = true;
}
}
// // TODO: substituer le finaliseur uniquement si 'Dispose(bool disposing)' a du code pour libérer les ressources non managées
// ~PlantUmlProcessManager()
// {
// // Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)'
// Dispose(disposing: false);
// }
public void Dispose()
{
// Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)'
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}