Handle BizTalk artifact collection via ExplorerOM
Build und Test / build (push) Has been cancelled
Build und Test / build (push) Has been cancelled
This commit is contained in:
+25
-19
@@ -41,9 +41,10 @@ Administrative cmd.exe
|
|||||||
| +-- MSBTS_GroupSetting für Management SQL/DB
|
| +-- MSBTS_GroupSetting für Management SQL/DB
|
||||||
|
|
|
|
||||||
+-- BizTalkWmiCollector
|
+-- BizTalkWmiCollector
|
||||||
| +-- ExplorerOM: vollständige Anwendungsliste
|
| +-- ExplorerOM: Anwendungen, Assemblies, Schemas,
|
||||||
|
| | Maps und Pipelines
|
||||||
| +-- root\MicrosoftBizTalkServer
|
| +-- root\MicrosoftBizTalkServer
|
||||||
| +-- optionale Artefakte, Adapter, Hosts und Handler
|
| +-- Orchestrierungen, Ports, Adapter, Hosts und Handler
|
||||||
| +-- optionaler SAP-Endpunkt-Fallback
|
| +-- optionaler SAP-Endpunkt-Fallback
|
||||||
|
|
|
|
||||||
+-- BindingExportCollector
|
+-- BindingExportCollector
|
||||||
@@ -104,8 +105,9 @@ SELECT Name,MgmtDbServerName,MgmtDbName FROM MSBTS_GroupSetting
|
|||||||
Die vollständige Anwendungsliste wird anschließend über
|
Die vollständige Anwendungsliste wird anschließend über
|
||||||
`Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer` und integrierte
|
`Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer` und integrierte
|
||||||
Windows-Authentifizierung aus der BizTalk Management Database gelesen. Es werden
|
Windows-Authentifizierung aus der BizTalk Management Database gelesen. Es werden
|
||||||
keine ändernden ExplorerOM-Methoden aufgerufen. Die Connection String wird nicht
|
keine ändernden ExplorerOM-Methoden aufgerufen. Die bereitgestellten Assemblies
|
||||||
protokolliert.
|
und deren Schemas, Transforms/Maps und Pipelines werden aus denselben
|
||||||
|
Anwendungsobjekten gelesen. Die Connection String wird nicht protokolliert.
|
||||||
|
|
||||||
Dieser Aufbau vermeidet die nicht verlässlich vorhandene WMI-Klasse
|
Dieser Aufbau vermeidet die nicht verlässlich vorhandene WMI-Klasse
|
||||||
`MSBTS_Application`, die je nach Installation mit `Invalid class` abbrechen kann.
|
`MSBTS_Application`, die je nach Installation mit `Invalid class` abbrechen kann.
|
||||||
@@ -114,7 +116,7 @@ ExplorerOM-Assemblyname, Version und Dateipfad werden protokolliert. Wenn
|
|||||||
`--management-server`/`--management-database` ermittelte Datenbankziel weiterhin
|
`--management-server`/`--management-database` ermittelte Datenbankziel weiterhin
|
||||||
für ExplorerOM verwendet werden.
|
für ExplorerOM verwendet werden.
|
||||||
|
|
||||||
### 3.3 Optionale BizTalk-WMI-Daten
|
### 3.3 BizTalk-WMI-Laufzeitdaten
|
||||||
|
|
||||||
Primärer Namespace:
|
Primärer Namespace:
|
||||||
|
|
||||||
@@ -122,31 +124,31 @@ Primärer Namespace:
|
|||||||
root\MicrosoftBizTalkServer
|
root\MicrosoftBizTalkServer
|
||||||
```
|
```
|
||||||
|
|
||||||
Optionale beziehungsweise versionsabhängige Artefaktklassen:
|
Verwendete BizTalk-WMI-Klassen:
|
||||||
|
|
||||||
- `MSBTS_Orchestration`
|
- `MSBTS_Orchestration`
|
||||||
- `MSBTS_SendPort`
|
- `MSBTS_SendPort`
|
||||||
- `MSBTS_SendPortGroup`
|
- `MSBTS_SendPortGroup`
|
||||||
- `MSBTS_ReceivePort`
|
- `MSBTS_ReceivePort`
|
||||||
- `MSBTS_ReceiveLocation`
|
- `MSBTS_ReceiveLocation`
|
||||||
- `MSBTS_Assembly`
|
|
||||||
- `MSBTS_Schema`
|
|
||||||
- `MSBTS_Map`
|
|
||||||
- `MSBTS_Pipeline`
|
|
||||||
- `MSBTS_AdapterSetting`
|
- `MSBTS_AdapterSetting`
|
||||||
- `MSBTS_ReceiveHandler`
|
- `MSBTS_ReceiveHandler`
|
||||||
- `MSBTS_SendHandler2` bzw. `MSBTS_SendHandler`
|
- `MSBTS_SendHandler2` bzw. `MSBTS_SendHandler`
|
||||||
- `MSBTS_HostSetting`
|
- `MSBTS_HostSetting`
|
||||||
- `MSBTS_HostInstance`
|
- `MSBTS_HostInstance`
|
||||||
|
|
||||||
Eine fehlende optionale Klasse erzeugt einen Hinweis, verhindert aber nicht die
|
`MSBTS_Assembly`, `MSBTS_Schema`, `MSBTS_Map` und `MSBTS_Pipeline` werden bewusst
|
||||||
ExplorerOM-Anwendungsliste oder den Word-Bericht. Ist der gesamte BizTalk-WMI-
|
nicht abgefragt: Sie waren auf ACC nicht registriert und gehören nicht zu den
|
||||||
Namespace für diese Zusatzdaten nicht erreichbar, wird der WMI-Teil übersprungen;
|
dokumentierten WMI-Kernklassen. Die entsprechenden Daten stammen aus ExplorerOM.
|
||||||
die zuvor geladene Anwendungsliste bleibt erhalten.
|
|
||||||
|
|
||||||
Zum Schutz vor ungewöhnlich großen Artefaktmengen begrenzt `MaxArtifactDetailsPerType`
|
Eine fehlende optionale Laufzeitklasse erzeugt einen Hinweis, verhindert aber
|
||||||
die aufgenommenen Detaildatensätze pro WMI-Klasse auf standardmäßig 5000. Die
|
nicht die ExplorerOM-Anwendungsliste oder den Word-Bericht. Ist der gesamte
|
||||||
Anwendungsliste selbst wird nicht begrenzt.
|
BizTalk-WMI-Namespace für diese Zusatzdaten nicht erreichbar, wird der WMI-Teil
|
||||||
|
übersprungen; die zuvor geladene Anwendungsliste bleibt erhalten.
|
||||||
|
|
||||||
|
Zum Schutz vor ungewöhnlich großen Artefaktmengen begrenzt
|
||||||
|
`MaxArtifactDetailsPerType` die aufgenommenen Detaildatensätze je Artefakttyp auf
|
||||||
|
standardmäßig 5000. Die Anwendungsliste selbst wird nicht begrenzt.
|
||||||
|
|
||||||
### 3.4 BTSTask-Gruppenbinding
|
### 3.4 BTSTask-Gruppenbinding
|
||||||
|
|
||||||
@@ -337,13 +339,16 @@ Das DR-Runbook muss mindestens enthalten:
|
|||||||
- WMI-Timeout, WQL-, Laufzeit- und Datensatzprotokollierung
|
- WMI-Timeout, WQL-, Laufzeit- und Datensatzprotokollierung
|
||||||
- vollständige Anwendungsliste über read-only ExplorerOM statt
|
- vollständige Anwendungsliste über read-only ExplorerOM statt
|
||||||
`MSBTS_Application`
|
`MSBTS_Application`
|
||||||
|
- Assemblies, Schemas, Maps und Pipelines über ExplorerOM statt nicht
|
||||||
|
registrierter WMI-Klassen
|
||||||
|
- Reflection unterstützt auch explizit implementierte ExplorerOM-Interfaces
|
||||||
- dynamische ExplorerOM-Auflösung aus GAC und BizTalk-Installationspfad
|
- dynamische ExplorerOM-Auflösung aus GAC und BizTalk-Installationspfad
|
||||||
- WMI-Zusatzdaten dürfen nach erfolgreicher ExplorerOM-Erfassung ausfallen
|
- WMI-Zusatzdaten dürfen nach erfolgreicher ExplorerOM-Erfassung ausfallen
|
||||||
- Prozess-Timeout für `BTSTask`
|
- Prozess-Timeout für `BTSTask`
|
||||||
- live gespiegelte, secret-bereinigte BTSTask-Ausgabe
|
- live gespiegelte, secret-bereinigte BTSTask-Ausgabe
|
||||||
- WMI-Fallback für SAP-Endpunkte
|
- WMI-Fallback für SAP-Endpunkte ohne doppelte Port-Abfragen
|
||||||
- Offline-Bindingmodus
|
- Offline-Bindingmodus
|
||||||
- optionale WMI-Klassen stoppen den Lauf nicht
|
- optionale Zugriffsfehler werden als Warnung protokolliert und stoppen den Lauf nicht
|
||||||
- begrenzte Dateisystemsuche
|
- begrenzte Dateisystemsuche
|
||||||
- Fehlerisolation bei Registry, Zertifikaten und Dateimetadaten
|
- Fehlerisolation bei Registry, Zertifikaten und Dateimetadaten
|
||||||
- XML-Parser ohne DTD/External Entities
|
- XML-Parser ohne DTD/External Entities
|
||||||
@@ -420,6 +425,7 @@ Tests prüfen:
|
|||||||
- SAP-Client, Host und `UseSnc`
|
- SAP-Client, Host und `UseSnc`
|
||||||
- Anwendungszuordnung
|
- Anwendungszuordnung
|
||||||
- Diagnose-Logging mit HRESULT und innerer Reflection-Ausnahme
|
- Diagnose-Logging mit HRESULT und innerer Reflection-Ausnahme
|
||||||
|
- ExplorerOM-Artefakte einschließlich expliziter Interface-Eigenschaften
|
||||||
- DOCX-Paketstruktur
|
- DOCX-Paketstruktur
|
||||||
- XML-Gültigkeit aller DOCX-Parts
|
- XML-Gültigkeit aller DOCX-Parts
|
||||||
- Ausschluss von Testkennwörtern
|
- Ausschluss von Testkennwörtern
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ BizTalk-Installationspfad geladen.
|
|||||||
- Anzahl und Zuordnung von Orchestrierungen
|
- Anzahl und Zuordnung von Orchestrierungen
|
||||||
- Send Ports und Send Port Groups
|
- Send Ports und Send Port Groups
|
||||||
- Receive Ports und Receive Locations
|
- Receive Ports und Receive Locations
|
||||||
- Assemblies, Schemas, Maps und Pipelines, soweit die jeweilige WMI-Klasse verfügbar ist
|
- Assemblies, Schemas, Maps und Pipelines aus ExplorerOM
|
||||||
- SAP-/IDoc-Indikatoren aus bereitgestellten Schemas, Actions und Artefaktnamen
|
- SAP-/IDoc-Indikatoren aus bereitgestellten Schemas, Actions und Artefaktnamen
|
||||||
|
|
||||||
Die Liste ist nicht auf eine bisher bekannte 15-App-Liste begrenzt. Damit werden auch weitere Anwendungen wie beispielsweise `MasterData_SAP_WebGIS`, `MasterDataHeat`, `WoWHeatInvoice` oder `MeterChangeInfo/Order` sichtbar, sofern sie tatsächlich in der jeweiligen BizTalk-Gruppe installiert sind.
|
Die Liste ist nicht auf eine bisher bekannte 15-App-Liste begrenzt. Damit werden auch weitere Anwendungen wie beispielsweise `MasterData_SAP_WebGIS`, `MasterDataHeat`, `WoWHeatInvoice` oder `MeterChangeInfo/Order` sichtbar, sofern sie tatsächlich in der jeweiligen BizTalk-Gruppe installiert sind.
|
||||||
@@ -139,6 +139,7 @@ Das Tool zeigt jeden Abschnitt und die erzeugten Dateien direkt auf der Konsole
|
|||||||
[INFO] Suche die lokale Assembly Microsoft.BizTalk.ExplorerOM.dll.
|
[INFO] Suche die lokale Assembly Microsoft.BizTalk.ExplorerOM.dll.
|
||||||
[INFO] ExplorerOM-Assembly geladen: Microsoft.BizTalk.ExplorerOM, Version=...
|
[INFO] ExplorerOM-Assembly geladen: Microsoft.BizTalk.ExplorerOM, Version=...
|
||||||
[INFO] 34 BizTalk-Anwendung(en) geladen (412 ms).
|
[INFO] 34 BizTalk-Anwendung(en) geladen (412 ms).
|
||||||
|
[INFO] ExplorerOM Schema: 87 Artefakt(e).
|
||||||
[INFO] Verbinde lokal mit dem BizTalk-WMI-Namespace für optionale Zusatzdaten.
|
[INFO] Verbinde lokal mit dem BizTalk-WMI-Namespace für optionale Zusatzdaten.
|
||||||
[INFO] WMI-Abfrage abgeschlossen: SendPort; Datensätze=91; Dauer=173 ms.
|
[INFO] WMI-Abfrage abgeschlossen: SendPort; Datensätze=91; Dauer=173 ms.
|
||||||
[INFO] Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.
|
[INFO] Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.
|
||||||
@@ -147,11 +148,12 @@ Das Tool zeigt jeden Abschnitt und die erzeugten Dateien direkt auf der Konsole
|
|||||||
[INFO] Ergebnisübersicht: Anwendungen=34; SAP-Endpunkte=12; Findings=4.
|
[INFO] Ergebnisübersicht: Anwendungen=34; SAP-Endpunkte=12; Findings=4.
|
||||||
```
|
```
|
||||||
|
|
||||||
Konsole und Log erhalten dieselben Fortschritts- und Diagnosemeldungen. Fehler
|
Konsole und Log erhalten dieselben Fortschritts- und Diagnosemeldungen.
|
||||||
enthalten Ausnahmetyp, HRESULT, WMI-Status, innere Ausnahmen und Stacktrace.
|
Pflichtfehler erscheinen als `[FEHLER]`, durch einen Fallback abgedeckte oder
|
||||||
Fehler eines Collectors stoppen die übrigen Abschnitte nicht. Fehlende optionale
|
optionale Zugriffsfehler als `[WARNUNG]`. Ausnahmedetails enthalten Typ, HRESULT,
|
||||||
WMI-Klassen verwerfen insbesondere nicht die über ExplorerOM geladene
|
WMI-Status, innere Ausnahmen und Stacktrace. Fehler eines Collectors stoppen die
|
||||||
Anwendungsliste.
|
übrigen Abschnitte nicht; Fehler optionaler Zusatzdaten verwerfen insbesondere
|
||||||
|
nicht die über ExplorerOM geladene Anwendungsliste.
|
||||||
|
|
||||||
## Ausgabedateien
|
## Ausgabedateien
|
||||||
|
|
||||||
@@ -226,10 +228,12 @@ Die Gitea-Workflowdatei `.gitea/workflows/build.yml` baut, testet und veröffent
|
|||||||
### WMI meldet `Invalid class`
|
### WMI meldet `Invalid class`
|
||||||
|
|
||||||
Die Anwendungsliste verwendet nicht mehr die nicht verlässlich vorhandene Klasse
|
Die Anwendungsliste verwendet nicht mehr die nicht verlässlich vorhandene Klasse
|
||||||
`MSBTS_Application`, sondern ExplorerOM. Ein `Invalid class` für eine optionale
|
`MSBTS_Application`, sondern ExplorerOM. Auch Assemblies, Schemas, Maps und
|
||||||
Artefaktklasse erscheint mit WQL, HRESULT und WMI-Status im Log, wird als Finding
|
Pipelines werden über ExplorerOM gelesen, weil `MSBTS_Assembly`, `MSBTS_Schema`,
|
||||||
übernommen und der Lauf wird fortgesetzt. Schlägt bereits
|
`MSBTS_Map` und `MSBTS_Pipeline` nicht zu den verlässlich registrierten
|
||||||
`MSBTS_GroupSetting` fehl, kann die Management-Datenbank über die beiden
|
BizTalk-WMI-Kernklassen gehören. Verbleibende optionale WMI-Fehler erscheinen mit
|
||||||
|
WQL, HRESULT und WMI-Status als `[WARNUNG]`; der Lauf wird fortgesetzt. Schlägt
|
||||||
|
bereits `MSBTS_GroupSetting` fehl, kann die Management-Datenbank über die beiden
|
||||||
`--management-*`-Optionen vorgegeben werden.
|
`--management-*`-Optionen vorgegeben werden.
|
||||||
|
|
||||||
### ExplorerOM-Assembly fehlt
|
### ExplorerOM-Assembly fehlt
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ using BizTalkSapEnvironmentInventory.Models;
|
|||||||
namespace BizTalkSapEnvironmentInventory.Collectors
|
namespace BizTalkSapEnvironmentInventory.Collectors
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Liest die vollständige Anwendungsliste über ExplorerOM und ergänzt sie um
|
/// Liest Anwendungen sowie bereitgestellte Assembly-Artefakte über ExplorerOM
|
||||||
/// optionale Artefakt-, Adapter- und Hostdaten aus dem BizTalk-WMI-Provider.
|
/// und ergänzt sie um die vom BizTalk-WMI-Provider angebotenen Laufzeitdaten.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class BizTalkWmiCollector
|
internal sealed class BizTalkWmiCollector
|
||||||
{
|
{
|
||||||
@@ -36,11 +36,31 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
ConsoleFileLogger logger,
|
ConsoleFileLogger logger,
|
||||||
int timeoutSeconds,
|
int timeoutSeconds,
|
||||||
int maxArtifactDetailsPerType)
|
int maxArtifactDetailsPerType)
|
||||||
|
: this(
|
||||||
|
document,
|
||||||
|
logger,
|
||||||
|
timeoutSeconds,
|
||||||
|
maxArtifactDetailsPerType,
|
||||||
|
true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private BizTalkWmiCollector(
|
||||||
|
InventoryDocument document,
|
||||||
|
ConsoleFileLogger logger,
|
||||||
|
int timeoutSeconds,
|
||||||
|
int maxArtifactDetailsPerType,
|
||||||
|
bool initializeWmi)
|
||||||
{
|
{
|
||||||
this.document = document;
|
this.document = document;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
||||||
this.maxArtifactDetailsPerType = Math.Max(100, maxArtifactDetailsPerType);
|
this.maxArtifactDetailsPerType = Math.Max(100, maxArtifactDetailsPerType);
|
||||||
|
if (!initializeWmi)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scope = new ManagementScope(WmiNamespace);
|
scope = new ManagementScope(WmiNamespace);
|
||||||
scope.Options.Timeout = timeout;
|
scope.Options.Timeout = timeout;
|
||||||
scope.Options.EnablePrivileges = false;
|
scope.Options.EnablePrivileges = false;
|
||||||
@@ -110,6 +130,8 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
logger.Info(applications.Count + " BizTalk-Anwendung(en) geladen ("
|
logger.Info(applications.Count + " BizTalk-Anwendung(en) geladen ("
|
||||||
+ timer.ElapsedMilliseconds + " ms).");
|
+ timer.ElapsedMilliseconds + " ms).");
|
||||||
|
|
||||||
|
var explorerArtifactCounts =
|
||||||
|
new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var item in applications)
|
foreach (var item in applications)
|
||||||
{
|
{
|
||||||
var name = RequiredText(item, "Name");
|
var name = RequiredText(item, "Name");
|
||||||
@@ -118,6 +140,10 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
application.Status = FormatStatus(OptionalText(item, "Status"));
|
application.Status = FormatStatus(OptionalText(item, "Status"));
|
||||||
logger.Detail("ExplorerOM-Anwendung: " + name
|
logger.Detail("ExplorerOM-Anwendung: " + name
|
||||||
+ "; Status=" + application.Status + ".");
|
+ "; Status=" + application.Status + ".");
|
||||||
|
CollectExplorerArtifacts(
|
||||||
|
item,
|
||||||
|
application,
|
||||||
|
explorerArtifactCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.Applications.Count == 0)
|
if (document.Applications.Count == 0)
|
||||||
@@ -125,6 +151,14 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
"Das BizTalk Explorer Object Model lieferte keine Anwendungen.");
|
"Das BizTalk Explorer Object Model lieferte keine Anwendungen.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var artifactType in new[] { "Assembly", "Schema", "Map", "Pipeline" })
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
explorerArtifactCounts.TryGetValue(artifactType, out count);
|
||||||
|
logger.Info("ExplorerOM " + artifactType + ": "
|
||||||
|
+ count + " Artefakt(e).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -145,11 +179,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
new ClassDescriptor("MSBTS_SendPort", "SendPort"),
|
new ClassDescriptor("MSBTS_SendPort", "SendPort"),
|
||||||
new ClassDescriptor("MSBTS_SendPortGroup", "SendPortGroup"),
|
new ClassDescriptor("MSBTS_SendPortGroup", "SendPortGroup"),
|
||||||
new ClassDescriptor("MSBTS_ReceivePort", "ReceivePort"),
|
new ClassDescriptor("MSBTS_ReceivePort", "ReceivePort"),
|
||||||
new ClassDescriptor("MSBTS_ReceiveLocation", "ReceiveLocation"),
|
new ClassDescriptor("MSBTS_ReceiveLocation", "ReceiveLocation")
|
||||||
new ClassDescriptor("MSBTS_Assembly", "Assembly"),
|
|
||||||
new ClassDescriptor("MSBTS_Schema", "Schema"),
|
|
||||||
new ClassDescriptor("MSBTS_Map", "Map"),
|
|
||||||
new ClassDescriptor("MSBTS_Pipeline", "Pipeline")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var descriptor in artifactClasses)
|
foreach (var descriptor in artifactClasses)
|
||||||
@@ -165,9 +195,6 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
TryCollectComponents("MSBTS_HostSetting", "Host", document.Hosts, false);
|
TryCollectComponents("MSBTS_HostSetting", "Host", document.Hosts, false);
|
||||||
TryCollectComponents("MSBTS_HostInstance", "HostInstance", document.Hosts, false);
|
TryCollectComponents("MSBTS_HostInstance", "HostInstance", document.Hosts, false);
|
||||||
|
|
||||||
TryCollectWmiSapEndpoints("MSBTS_SendPort", "Senden");
|
|
||||||
TryCollectWmiSapEndpoints("MSBTS_ReceiveLocation", "Empfangen");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogRuntimeInformation()
|
private void LogRuntimeInformation()
|
||||||
@@ -233,7 +260,9 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
+ "Die über ExplorerOM geladene Anwendungsliste bleibt verwendbar.",
|
+ "Die über ExplorerOM geladene Anwendungsliste bleibt verwendbar.",
|
||||||
Owner = "BizTalk-Betrieb"
|
Owner = "BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Exception("BizTalk-WMI-Verbindung fehlgeschlagen", exception);
|
logger.WarningException(
|
||||||
|
"BizTalk-WMI-Verbindung fehlgeschlagen",
|
||||||
|
exception);
|
||||||
logger.Warning(
|
logger.Warning(
|
||||||
"Optionale WMI-Zusatzdaten werden übersprungen; "
|
"Optionale WMI-Zusatzdaten werden übersprungen; "
|
||||||
+ "die ExplorerOM-Anwendungsliste bleibt erhalten.");
|
+ "die ExplorerOM-Anwendungsliste bleibt erhalten.");
|
||||||
@@ -424,6 +453,24 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPort und ReceiveLocation werden bereits hier gelesen.
|
||||||
|
// Der SAP-Fallback nutzt dieselben Zeilen und belastet den
|
||||||
|
// WMI-Provider daher nicht mit einer zweiten Vollabfrage.
|
||||||
|
if (string.Equals(
|
||||||
|
descriptor.ClassName,
|
||||||
|
"MSBTS_SendPort",
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
TryAddWmiSapEndpoint(row, "Senden");
|
||||||
|
}
|
||||||
|
else if (string.Equals(
|
||||||
|
descriptor.ClassName,
|
||||||
|
"MSBTS_ReceiveLocation",
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
TryAddWmiSapEndpoint(row, "Empfangen");
|
||||||
|
}
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,61 +528,44 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryCollectWmiSapEndpoints(string className, string direction)
|
private void TryAddWmiSapEndpoint(ManagementObject row, string direction)
|
||||||
{
|
{
|
||||||
List<ManagementObject> rows = null;
|
if (!LooksLikeSap(row))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
rows = Query(
|
return;
|
||||||
className + " SAP-Fallback",
|
}
|
||||||
"SELECT * FROM " + className);
|
|
||||||
foreach (var row in rows)
|
|
||||||
{
|
|
||||||
if (!LooksLikeSap(row))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var endpointName = First(row, "Name");
|
var endpointName = First(row, "Name");
|
||||||
if (document.SapEndpoints.Any(item =>
|
if (string.IsNullOrWhiteSpace(endpointName)
|
||||||
string.Equals(item.Direction, direction, StringComparison.OrdinalIgnoreCase)
|
|| document.SapEndpoints.Any(item =>
|
||||||
&& string.Equals(item.Name, endpointName, StringComparison.OrdinalIgnoreCase)))
|
string.Equals(item.Direction, direction, StringComparison.OrdinalIgnoreCase)
|
||||||
{
|
&& string.Equals(item.Name, endpointName, StringComparison.OrdinalIgnoreCase)))
|
||||||
continue;
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var endpoint = new SapEndpointRecord
|
var endpoint = new SapEndpointRecord
|
||||||
{
|
|
||||||
ApplicationName = First(row, "ApplicationName"),
|
|
||||||
Direction = direction,
|
|
||||||
Name = endpointName,
|
|
||||||
ParentPortName = First(row, "ReceivePortName"),
|
|
||||||
AdapterName = First(
|
|
||||||
row,
|
|
||||||
"PTTransportType",
|
|
||||||
"AdapterName",
|
|
||||||
"TransportType"),
|
|
||||||
HostName = First(row, "HostName", "SendHandler", "ReceiveHandler"),
|
|
||||||
Status = FormatStatus(First(row, "Status", "IsDisabled")),
|
|
||||||
Address = SensitiveDataSanitizer.SanitizeText(First(
|
|
||||||
row,
|
|
||||||
"PTAddress",
|
|
||||||
"InboundTransportURL",
|
|
||||||
"Address")),
|
|
||||||
Source = "WMI-Fallback"
|
|
||||||
};
|
|
||||||
AddSelectedProperties(row, endpoint.Properties);
|
|
||||||
document.SapEndpoints.Add(endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
|
||||||
{
|
{
|
||||||
AddOptionalWmiFinding(className + " SAP-Fallback", exception);
|
ApplicationName = First(row, "ApplicationName"),
|
||||||
}
|
Direction = direction,
|
||||||
finally
|
Name = endpointName,
|
||||||
{
|
ParentPortName = First(row, "ReceivePortName"),
|
||||||
DisposeRows(rows);
|
AdapterName = First(
|
||||||
}
|
row,
|
||||||
|
"PTTransportType",
|
||||||
|
"AdapterName",
|
||||||
|
"TransportType"),
|
||||||
|
HostName = First(row, "HostName", "SendHandler", "ReceiveHandler"),
|
||||||
|
Status = FormatStatus(First(row, "Status", "IsDisabled")),
|
||||||
|
Address = SensitiveDataSanitizer.SanitizeText(First(
|
||||||
|
row,
|
||||||
|
"PTAddress",
|
||||||
|
"InboundTransportURL",
|
||||||
|
"Address")),
|
||||||
|
Source = "WMI-Fallback"
|
||||||
|
};
|
||||||
|
AddSelectedProperties(row, endpoint.Properties);
|
||||||
|
document.SapEndpoints.Add(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ManagementObject> Query(string name, string query)
|
private List<ManagementObject> Query(string name, string query)
|
||||||
@@ -591,6 +621,247 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Liest Assemblies und deren Schemas, Maps und Pipelines über ExplorerOM.
|
||||||
|
/// Diese Objekte sind keine verlässlich verfügbaren BizTalk-WMI-Klassen.
|
||||||
|
/// Fehler in den Zusatzsammlungen dürfen die Anwendungsliste nicht verwerfen.
|
||||||
|
/// </summary>
|
||||||
|
private void CollectExplorerArtifacts(
|
||||||
|
object applicationObject,
|
||||||
|
ApplicationRecord application,
|
||||||
|
IDictionary<string, int> counts)
|
||||||
|
{
|
||||||
|
foreach (var assembly in EnumerateOptionalProperty(
|
||||||
|
applicationObject,
|
||||||
|
"Assemblies",
|
||||||
|
"Assemblies der Anwendung " + application.Name))
|
||||||
|
{
|
||||||
|
var assemblyName = FirstOptionalText(assembly, "DisplayName", "Name");
|
||||||
|
TryAddExplorerArtifact(
|
||||||
|
application,
|
||||||
|
counts,
|
||||||
|
"Assembly",
|
||||||
|
assemblyName,
|
||||||
|
string.Empty);
|
||||||
|
|
||||||
|
CollectExplorerAssemblyArtifacts(
|
||||||
|
assembly,
|
||||||
|
application,
|
||||||
|
counts,
|
||||||
|
"Schemas",
|
||||||
|
"Schema",
|
||||||
|
assemblyName);
|
||||||
|
CollectExplorerAssemblyArtifacts(
|
||||||
|
assembly,
|
||||||
|
application,
|
||||||
|
counts,
|
||||||
|
"Transforms",
|
||||||
|
"Map",
|
||||||
|
assemblyName);
|
||||||
|
CollectExplorerAssemblyArtifacts(
|
||||||
|
assembly,
|
||||||
|
application,
|
||||||
|
counts,
|
||||||
|
"Pipelines",
|
||||||
|
"Pipeline",
|
||||||
|
assemblyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectExplorerAssemblyArtifacts(
|
||||||
|
object assembly,
|
||||||
|
ApplicationRecord application,
|
||||||
|
IDictionary<string, int> counts,
|
||||||
|
string collectionProperty,
|
||||||
|
string artifactType,
|
||||||
|
string assemblyName)
|
||||||
|
{
|
||||||
|
foreach (var item in EnumerateOptionalProperty(
|
||||||
|
assembly,
|
||||||
|
collectionProperty,
|
||||||
|
collectionProperty + " aus Assembly " + assemblyName))
|
||||||
|
{
|
||||||
|
var name = FirstOptionalText(
|
||||||
|
item,
|
||||||
|
"FullName",
|
||||||
|
"AssemblyQualifiedName",
|
||||||
|
"Name");
|
||||||
|
TryAddExplorerArtifact(
|
||||||
|
application,
|
||||||
|
counts,
|
||||||
|
artifactType,
|
||||||
|
name,
|
||||||
|
assemblyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryAddExplorerArtifact(
|
||||||
|
ApplicationRecord application,
|
||||||
|
IDictionary<string, int> counts,
|
||||||
|
string artifactType,
|
||||||
|
string name,
|
||||||
|
string assemblyName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
logger.Detail("ExplorerOM-" + artifactType
|
||||||
|
+ " ohne auswertbaren Namen wurde übersprungen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count;
|
||||||
|
counts.TryGetValue(artifactType, out count);
|
||||||
|
if (count >= maxArtifactDetailsPerType)
|
||||||
|
{
|
||||||
|
AddArtifactLimitFindingOnce(artifactType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifact = new ArtifactRecord
|
||||||
|
{
|
||||||
|
Type = artifactType,
|
||||||
|
Name = name.Trim(),
|
||||||
|
ApplicationName = application.Name,
|
||||||
|
Status = "Bereitgestellt"
|
||||||
|
};
|
||||||
|
artifact.Properties.Add(new NameValueRecord(
|
||||||
|
"Quelle",
|
||||||
|
"BizTalk Explorer Object Model",
|
||||||
|
"ExplorerOM"));
|
||||||
|
if (!string.IsNullOrWhiteSpace(assemblyName)
|
||||||
|
&& !string.Equals(name, assemblyName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
artifact.Properties.Add(new NameValueRecord(
|
||||||
|
"Assembly",
|
||||||
|
assemblyName,
|
||||||
|
"ExplorerOM"));
|
||||||
|
}
|
||||||
|
|
||||||
|
application.Artifacts.Add(artifact);
|
||||||
|
counts[artifactType] = count + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddArtifactLimitFindingOnce(string artifactType)
|
||||||
|
{
|
||||||
|
if (document.Findings.Any(item =>
|
||||||
|
string.Equals(item.Area, artifactType, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& item.Message.IndexOf(
|
||||||
|
"Artefaktdetails wurden bei",
|
||||||
|
StringComparison.OrdinalIgnoreCase) >= 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.Findings.Add(new Finding
|
||||||
|
{
|
||||||
|
Severity = "Warnung",
|
||||||
|
Area = artifactType,
|
||||||
|
Message = "Artefaktdetails wurden bei "
|
||||||
|
+ maxArtifactDetailsPerType
|
||||||
|
+ " Einträgen begrenzt.",
|
||||||
|
RecommendedAction =
|
||||||
|
"MaxArtifactDetailsPerType bei Bedarf kontrolliert erhöhen.",
|
||||||
|
Owner = "BizTalk-Betrieb"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<object> EnumerateOptionalProperty(
|
||||||
|
object instance,
|
||||||
|
string propertyName,
|
||||||
|
string description)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var property = FindProperty(instance, propertyName);
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
throw new MissingMemberException(
|
||||||
|
instance.GetType().FullName,
|
||||||
|
propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var enumerable = property.GetValue(instance, null) as IEnumerable;
|
||||||
|
if (enumerable == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
description + " ist keine aufzählbare Sammlung.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<object>();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
result.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (IsOptionalExplorerException(exception))
|
||||||
|
{
|
||||||
|
AddOptionalExplorerFinding(propertyName, description, exception);
|
||||||
|
return new List<object>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOptionalExplorerFinding(
|
||||||
|
string propertyName,
|
||||||
|
string description,
|
||||||
|
Exception exception)
|
||||||
|
{
|
||||||
|
var area = "ExplorerOM " + propertyName;
|
||||||
|
if (!document.Findings.Any(item =>
|
||||||
|
string.Equals(item.Area, area, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
document.Findings.Add(new Finding
|
||||||
|
{
|
||||||
|
Severity = "Warnung",
|
||||||
|
Area = area,
|
||||||
|
Message = description + " konnten nicht gelesen werden: "
|
||||||
|
+ exception.Message,
|
||||||
|
RecommendedAction =
|
||||||
|
"ExplorerOM-Version, Berechtigung und Management-Datenbankzugriff "
|
||||||
|
+ "anhand des Logs prüfen. Die Anwendungsliste bleibt verwendbar.",
|
||||||
|
Owner = "BizTalk-Betrieb"
|
||||||
|
});
|
||||||
|
logger.WarningException(
|
||||||
|
"Optionale ExplorerOM-Sammlung " + description
|
||||||
|
+ " konnte nicht gelesen werden",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Detail("Weitere optionale ExplorerOM-Sammlung "
|
||||||
|
+ description + " konnte nicht gelesen werden: "
|
||||||
|
+ exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wird vom Self-Test mit expliziten Interface-Eigenschaften ausgeführt.
|
||||||
|
internal void CollectExplorerArtifactsForTest(
|
||||||
|
object applicationObject,
|
||||||
|
ApplicationRecord application)
|
||||||
|
{
|
||||||
|
CollectExplorerArtifacts(
|
||||||
|
applicationObject,
|
||||||
|
application,
|
||||||
|
new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static BizTalkWmiCollector CreateForExplorerArtifactTest(
|
||||||
|
InventoryDocument document,
|
||||||
|
ConsoleFileLogger logger,
|
||||||
|
int maxArtifactDetailsPerType)
|
||||||
|
{
|
||||||
|
return new BizTalkWmiCollector(
|
||||||
|
document,
|
||||||
|
logger,
|
||||||
|
30,
|
||||||
|
maxArtifactDetailsPerType,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
private static string RequiredText(object instance, string propertyName)
|
private static string RequiredText(object instance, string propertyName)
|
||||||
{
|
{
|
||||||
var value = Convert.ToString(
|
var value = Convert.ToString(
|
||||||
@@ -607,9 +878,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
|
|
||||||
private string OptionalText(object instance, string propertyName)
|
private string OptionalText(object instance, string propertyName)
|
||||||
{
|
{
|
||||||
var property = instance.GetType().GetProperty(
|
var property = FindProperty(instance, propertyName);
|
||||||
propertyName,
|
|
||||||
BindingFlags.Instance | BindingFlags.Public);
|
|
||||||
if (property == null)
|
if (property == null)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
@@ -621,10 +890,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
property.GetValue(instance, null),
|
property.GetValue(instance, null),
|
||||||
CultureInfo.InvariantCulture) ?? string.Empty;
|
CultureInfo.InvariantCulture) ?? string.Empty;
|
||||||
}
|
}
|
||||||
catch (Exception exception) when (
|
catch (Exception exception) when (IsOptionalExplorerException(exception))
|
||||||
exception is TargetInvocationException
|
|
||||||
|| exception is COMException
|
|
||||||
|| exception is NotSupportedException)
|
|
||||||
{
|
{
|
||||||
logger.Detail("Optionale ExplorerOM-Eigenschaft "
|
logger.Detail("Optionale ExplorerOM-Eigenschaft "
|
||||||
+ instance.GetType().FullName
|
+ instance.GetType().FullName
|
||||||
@@ -636,11 +902,23 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string FirstOptionalText(object instance, params string[] propertyNames)
|
||||||
|
{
|
||||||
|
foreach (var propertyName in propertyNames)
|
||||||
|
{
|
||||||
|
var value = OptionalText(instance, propertyName);
|
||||||
|
if (!string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private static object RequiredProperty(object instance, string propertyName)
|
private static object RequiredProperty(object instance, string propertyName)
|
||||||
{
|
{
|
||||||
var property = instance.GetType().GetProperty(
|
var property = FindProperty(instance, propertyName);
|
||||||
propertyName,
|
|
||||||
BindingFlags.Instance | BindingFlags.Public);
|
|
||||||
if (property == null)
|
if (property == null)
|
||||||
{
|
{
|
||||||
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
||||||
@@ -654,9 +932,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
string propertyName,
|
string propertyName,
|
||||||
object value)
|
object value)
|
||||||
{
|
{
|
||||||
var property = instance.GetType().GetProperty(
|
var property = FindProperty(instance, propertyName);
|
||||||
propertyName,
|
|
||||||
BindingFlags.Instance | BindingFlags.Public);
|
|
||||||
if (property == null || !property.CanWrite)
|
if (property == null || !property.CanWrite)
|
||||||
{
|
{
|
||||||
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
||||||
@@ -665,6 +941,23 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
property.SetValue(instance, value, null);
|
property.SetValue(instance, value, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PropertyInfo FindProperty(object instance, string propertyName)
|
||||||
|
{
|
||||||
|
var flags = BindingFlags.Instance
|
||||||
|
| BindingFlags.Public
|
||||||
|
| BindingFlags.NonPublic;
|
||||||
|
var properties = instance.GetType().GetProperties(flags);
|
||||||
|
return properties.FirstOrDefault(item =>
|
||||||
|
string.Equals(
|
||||||
|
item.Name,
|
||||||
|
propertyName,
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
?? properties.FirstOrDefault(item =>
|
||||||
|
item.Name.EndsWith(
|
||||||
|
"." + propertyName,
|
||||||
|
StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
private ApplicationRecord GetOrCreateApplication(string name)
|
private ApplicationRecord GetOrCreateApplication(string name)
|
||||||
{
|
{
|
||||||
var application = document.Applications.FirstOrDefault(item =>
|
var application = document.Applications.FirstOrDefault(item =>
|
||||||
@@ -808,11 +1101,21 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
RecommendedAction = "Nur relevant, wenn die zugehörige Artefaktspalte vollständig benötigt wird.",
|
RecommendedAction = "Nur relevant, wenn die zugehörige Artefaktspalte vollständig benötigt wird.",
|
||||||
Owner = "BizTalk-Betrieb"
|
Owner = "BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Exception(
|
logger.WarningException(
|
||||||
"Optionale WMI-Abfrage " + className + " fehlgeschlagen",
|
"Optionale WMI-Abfrage " + className + " fehlgeschlagen",
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsOptionalExplorerException(Exception exception)
|
||||||
|
{
|
||||||
|
// Die Sammlungen sind Zusatzdaten. Auch versionsspezifische
|
||||||
|
// ExplorerOM-/SQL-Ausnahmen sollen daher nur diese Sammlung auslassen.
|
||||||
|
// Prozesskritische Laufzeitfehler werden weiterhin nicht abgefangen.
|
||||||
|
return !(exception is OutOfMemoryException)
|
||||||
|
&& !(exception is AccessViolationException)
|
||||||
|
&& !(exception is AppDomainUnloadedException);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsRecoverableWmiException(Exception exception)
|
private static bool IsRecoverableWmiException(Exception exception)
|
||||||
{
|
{
|
||||||
return exception is ManagementException
|
return exception is ManagementException
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
+ "die BizTalk-Erfassung wird fortgesetzt.",
|
+ "die BizTalk-Erfassung wird fortgesetzt.",
|
||||||
Owner = "Windows-/BizTalk-Betrieb"
|
Owner = "Windows-/BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Exception("Windows-WMI-Abfrage fehlgeschlagen", exception);
|
logger.WarningException(
|
||||||
|
"Windows-WMI-Abfrage fehlgeschlagen",
|
||||||
|
exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +241,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
+ "--management-Parameter können die Ermittlung übernehmen.",
|
+ "--management-Parameter können die Ermittlung übernehmen.",
|
||||||
Owner = "Windows-/BizTalk-Betrieb"
|
Owner = "Windows-/BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Exception(
|
logger.WarningException(
|
||||||
"BizTalk-Registryansicht " + view + " fehlgeschlagen",
|
"BizTalk-Registryansicht " + view + " fehlgeschlagen",
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
@@ -330,7 +332,9 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
document,
|
document,
|
||||||
"MSBTS_GroupSetting konnte für die Management-DB-Erkennung "
|
"MSBTS_GroupSetting konnte für die Management-DB-Erkennung "
|
||||||
+ "nicht gelesen werden: " + exception.Message);
|
+ "nicht gelesen werden: " + exception.Message);
|
||||||
logger.Exception("MSBTS_GroupSetting fehlgeschlagen", exception);
|
logger.WarningException(
|
||||||
|
"MSBTS_GroupSetting fehlgeschlagen",
|
||||||
|
exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,45 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
/// WMI-Status und Stacktrace, damit Providerfehler vor Ort analysierbar bleiben.
|
/// WMI-Status und Stacktrace, damit Providerfehler vor Ort analysierbar bleiben.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Exception(string context, Exception exception)
|
public void Exception(string context, Exception exception)
|
||||||
|
{
|
||||||
|
WriteException(context, exception, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Protokolliert eine vollständige Ausnahmekette als Warnung, wenn ein
|
||||||
|
/// optionaler oder durch einen Fallback abgedeckter Zugriff fehlschlägt.
|
||||||
|
/// </summary>
|
||||||
|
public void WarningException(string context, Exception exception)
|
||||||
|
{
|
||||||
|
WriteException(context, exception, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteException(
|
||||||
|
string context,
|
||||||
|
Exception exception,
|
||||||
|
bool warning)
|
||||||
{
|
{
|
||||||
if (exception == null)
|
if (exception == null)
|
||||||
{
|
{
|
||||||
Error(context + ": Unbekannter Fehler ohne Ausnahmeobjekt.");
|
if (warning)
|
||||||
|
{
|
||||||
|
Warning(context + ": Unbekannter Fehler ohne Ausnahmeobjekt.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(context + ": Unbekannter Fehler ohne Ausnahmeobjekt.");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error(context + ": " + exception.Message);
|
if (warning)
|
||||||
|
{
|
||||||
|
Warning(context + ": " + exception.Message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(context + ": " + exception.Message);
|
||||||
|
}
|
||||||
var current = exception;
|
var current = exception;
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
while (current != null)
|
while (current != null)
|
||||||
|
|||||||
@@ -63,7 +63,14 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
+ "und lokale BizTalk-Konfiguration prüfen.",
|
+ "und lokale BizTalk-Konfiguration prüfen.",
|
||||||
Owner = "BizTalk-Betrieb"
|
Owner = "BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Exception(name, exception);
|
if (required)
|
||||||
|
{
|
||||||
|
logger.Exception(name, exception);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.WarningException(name, exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -24,6 +25,7 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
{
|
{
|
||||||
TestSanitizer();
|
TestSanitizer();
|
||||||
TestBindingParser();
|
TestBindingParser();
|
||||||
|
TestExplorerArtifactReader();
|
||||||
TestLoggerDiagnostics();
|
TestLoggerDiagnostics();
|
||||||
TestDocxPackage();
|
TestDocxPackage();
|
||||||
}
|
}
|
||||||
@@ -238,6 +240,53 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void TestExplorerArtifactReader()
|
||||||
|
{
|
||||||
|
var directory = CreateTemporaryDirectory();
|
||||||
|
var logPath = Path.Combine(directory, "explorer-artifacts.log");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var document = new InventoryDocument();
|
||||||
|
var application = new ApplicationRecord
|
||||||
|
{
|
||||||
|
Name = "ExplorerOM-Test",
|
||||||
|
Status = "Gestartet"
|
||||||
|
};
|
||||||
|
using (var logger = new ConsoleFileLogger(logPath))
|
||||||
|
{
|
||||||
|
var collector = BizTalkWmiCollector.CreateForExplorerArtifactTest(
|
||||||
|
document,
|
||||||
|
logger,
|
||||||
|
5000);
|
||||||
|
collector.CollectExplorerArtifactsForTest(
|
||||||
|
new FakeApplication(new FakeAssembly(
|
||||||
|
"Test.Assembly, Version=1.0.0.0",
|
||||||
|
new FakeArtifact("Test.Schema"),
|
||||||
|
new FakeArtifact("Test.Map"),
|
||||||
|
new FakeArtifact("Test.Pipeline"))),
|
||||||
|
application);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(application.Count("Assembly") == 1,
|
||||||
|
"ExplorerOM-Assembly wurde nicht gelesen.");
|
||||||
|
Assert(application.Count("Schema") == 1,
|
||||||
|
"ExplorerOM-Schema wurde nicht gelesen.");
|
||||||
|
Assert(application.Count("Map") == 1,
|
||||||
|
"ExplorerOM-Map wurde nicht gelesen.");
|
||||||
|
Assert(application.Count("Pipeline") == 1,
|
||||||
|
"ExplorerOM-Pipeline wurde nicht gelesen.");
|
||||||
|
Assert(
|
||||||
|
application.Artifacts.All(item =>
|
||||||
|
item.Properties.Any(property =>
|
||||||
|
property.Value == "BizTalk Explorer Object Model")),
|
||||||
|
"ExplorerOM-Quelle fehlt an einem Artefakt.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DeleteDirectory(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void TestLoggerDiagnostics()
|
private static void TestLoggerDiagnostics()
|
||||||
{
|
{
|
||||||
var directory = CreateTemporaryDirectory();
|
var directory = CreateTemporaryDirectory();
|
||||||
@@ -254,7 +303,7 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
// Die absichtlich erzeugte Testausnahme soll den Self-Test
|
// Die absichtlich erzeugte Testausnahme soll den Self-Test
|
||||||
// auf der Konsole nicht wie einen echten Laufzeitfehler aussehen lassen.
|
// auf der Konsole nicht wie einen echten Laufzeitfehler aussehen lassen.
|
||||||
Console.SetOut(TextWriter.Null);
|
Console.SetOut(TextWriter.Null);
|
||||||
logger.Exception("Self-Test-Diagnose", exception);
|
logger.WarningException("Self-Test-Diagnose", exception);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -272,6 +321,10 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
Assert(
|
Assert(
|
||||||
log.Contains("HRESULT=0x"),
|
log.Contains("HRESULT=0x"),
|
||||||
"Logger dokumentierte den HRESULT nicht.");
|
"Logger dokumentierte den HRESULT nicht.");
|
||||||
|
Assert(
|
||||||
|
log.Contains("[WARNUNG] Self-Test-Diagnose")
|
||||||
|
&& !log.Contains("[FEHLER] Self-Test-Diagnose"),
|
||||||
|
"Optionale Ausnahme wurde nicht als Warnung protokolliert.");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -381,5 +434,79 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface IFakeApplication
|
||||||
|
{
|
||||||
|
IEnumerable Assemblies { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface IFakeAssembly
|
||||||
|
{
|
||||||
|
IEnumerable Schemas { get; }
|
||||||
|
IEnumerable Transforms { get; }
|
||||||
|
IEnumerable Pipelines { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeApplication : IFakeApplication
|
||||||
|
{
|
||||||
|
private readonly IEnumerable assemblies;
|
||||||
|
|
||||||
|
public FakeApplication(params object[] assemblies)
|
||||||
|
{
|
||||||
|
this.assemblies = assemblies;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExplorerOM stellt einige Sammlungen explizit über Interfaces bereit.
|
||||||
|
IEnumerable IFakeApplication.Assemblies
|
||||||
|
{
|
||||||
|
get { return assemblies; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeAssembly : IFakeAssembly
|
||||||
|
{
|
||||||
|
private readonly IEnumerable schemas;
|
||||||
|
private readonly IEnumerable transforms;
|
||||||
|
private readonly IEnumerable pipelines;
|
||||||
|
|
||||||
|
public FakeAssembly(
|
||||||
|
string displayName,
|
||||||
|
object schema,
|
||||||
|
object transform,
|
||||||
|
object pipeline)
|
||||||
|
{
|
||||||
|
DisplayName = displayName;
|
||||||
|
schemas = new[] { schema };
|
||||||
|
transforms = new[] { transform };
|
||||||
|
pipelines = new[] { pipeline };
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName { get; private set; }
|
||||||
|
|
||||||
|
IEnumerable IFakeAssembly.Schemas
|
||||||
|
{
|
||||||
|
get { return schemas; }
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable IFakeAssembly.Transforms
|
||||||
|
{
|
||||||
|
get { return transforms; }
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable IFakeAssembly.Pipelines
|
||||||
|
{
|
||||||
|
get { return pipelines; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeArtifact
|
||||||
|
{
|
||||||
|
public FakeArtifact(string fullName)
|
||||||
|
{
|
||||||
|
FullName = fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FullName { get; private set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,9 @@ namespace BizTalkSapEnvironmentInventory
|
|||||||
logger.Info("Server: " + Environment.MachineName);
|
logger.Info("Server: " + Environment.MachineName);
|
||||||
logger.Info("Ausgabeordner: " + options.OutputDirectory);
|
logger.Info("Ausgabeordner: " + options.OutputDirectory);
|
||||||
logger.Info(
|
logger.Info(
|
||||||
"Modus: read-only; ExplorerOM für Anwendungen, WMI für "
|
"Modus: read-only; ExplorerOM für Anwendungen und "
|
||||||
+ "Gruppenermittlung und optionale Artefakte.");
|
+ "bereitgestellte Assembly-Artefakte; WMI für "
|
||||||
|
+ "Gruppenermittlung und Laufzeitobjekte.");
|
||||||
logger.Detail("Logdatei: " + logPath);
|
logger.Detail("Logdatei: " + logPath);
|
||||||
logger.Detail("DOCX-Zieldatei: " + reportPath);
|
logger.Detail("DOCX-Zieldatei: " + reportPath);
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ namespace BizTalkSapEnvironmentInventory
|
|||||||
logger,
|
logger,
|
||||||
wmiTimeoutSeconds).Collect(document));
|
wmiTimeoutSeconds).Collect(document));
|
||||||
safeCollector.Execute(
|
safeCollector.Execute(
|
||||||
"BizTalk-Anwendungen und optionale WMI-Artefakte",
|
"BizTalk-Anwendungen und -Artefakte",
|
||||||
true,
|
true,
|
||||||
() => new BizTalkWmiCollector(
|
() => new BizTalkWmiCollector(
|
||||||
document,
|
document,
|
||||||
|
|||||||
@@ -168,9 +168,10 @@ namespace BizTalkSapEnvironmentInventory.Reporting
|
|||||||
WriteParagraph(
|
WriteParagraph(
|
||||||
writer,
|
writer,
|
||||||
"Die vollständige Anwendungsliste stammt aus dem read-only BizTalk Explorer "
|
"Die vollständige Anwendungsliste stammt aus dem read-only BizTalk Explorer "
|
||||||
+ "Object Model. Artefaktzahlen werden aus lokal verfügbaren, optionalen "
|
+ "Object Model. Assemblies, Schemas, Maps und Pipelines werden ebenfalls "
|
||||||
+ "BizTalk-WMI-Klassen gebildet; fehlende Klassen werden im Log und als "
|
+ "über ExplorerOM gelesen. Laufzeitobjekte wie Ports und Orchestrierungen "
|
||||||
+ "Finding ausgewiesen, ohne die Anwendungsliste zu verwerfen.",
|
+ "stammen aus den lokal verfügbaren BizTalk-WMI-Klassen. Fehler optionaler "
|
||||||
|
+ "Zusatzdaten verwerfen die Anwendungsliste nicht.",
|
||||||
"Normal",
|
"Normal",
|
||||||
false,
|
false,
|
||||||
null);
|
null);
|
||||||
|
|||||||
Reference in New Issue
Block a user