git_bsmd/bsmd.hisnord/Response.cs

334 lines
20 KiB
C#

// Copyright (c) 2015- schick Informatik
// Description: Bearbeitung von Antworten (dateibasiert..)
using bsmd.database;
using System;
using System.Collections.Generic;
using System.IO;
using log4net;
using System.Xml.Linq;
namespace bsmd.hisnord
{
public static class Response
{
private static readonly ILog _log = LogManager.GetLogger(typeof(Response));
public static void ReadAnswers()
{
string answerDir = Path.Combine(Properties.Settings.Default.TransmitterRoot, Properties.Settings.Default.AnswerDir);
foreach (string answerFile in Directory.GetFiles(answerDir))
{
bool isOK = true;
// bei Problemen die Antwort auf die Seite kopieren..
// File.Copy(answerFile, Path.Combine(@"C:\temp\hnord", Path.GetFileName(answerFile)));
// Informationen aus dem Dateinamen
// Meldetyp_Referenz_ID_Timestamp.xml
string bareFileName = Path.GetFileNameWithoutExtension(answerFile);
string[] fileNameElems = bareFileName.Split('_');
if (fileNameElems.Length < 4)
{
_log.WarnFormat("ANSWER file {0}.xml has an invalid file name", bareFileName);
isOK = false;
}
else
{
if (!Int32.TryParse(fileNameElems[fileNameElems.Length - 1], out int prozessStatus))
{
_log.WarnFormat("ANSWER file {0}.xml has no process status at the end (2..6)", bareFileName);
isOK = false;
}
else
{
if (!Int64.TryParse(fileNameElems[fileNameElems.Length - 2], out long timestampMilliSecs))
{
_log.WarnFormat("ANSWER file {0}.xml has no readable timestamp", bareFileName);
isOK = false;
}
else
{
string refId = fileNameElems[fileNameElems.Length - 3];
string meldeTyp = fileNameElems[fileNameElems.Length - 4];
if (fileNameElems.Length == 5)
meldeTyp = string.Format("{0}_{1}", fileNameElems[fileNameElems.Length - 5], meldeTyp);
// TODO: klären was man hier liest: reguläre Antwort oder Schnittstellenfehler
// XML Linq statt Serialisierung
try
{
XElement xml = XElement.Load(answerFile);
// declare Namespaces
XNamespace ns1 = "http://api.national-single-window.de/visitIdRequest";
XNamespace ns6 = "http://api.national-single-window.de/receipt";
XNamespace ns14 = "http://api.national-single-window.de/statusForClientRequestId";
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace ns15 = "http://api.national-single-window.de/visitIdResponse";
if (xml.Name == "SystemError")
{
// Fehlernachricht
SystemError systemError = SystemError.createFromXml(xml);
if (systemError != null)
{
MessageCore aCore = DBManager.Instance.GetMessageCoreById(systemError.MessageCoreId);
if (aCore != null)
{
if (Enum.TryParse<Message.NotificationClass>(systemError.Meldetype, out Message.NotificationClass notificationClass))
{
Message refMessage = DBManager.Instance.GetMessage(aCore, notificationClass);
if (refMessage != null)
{
refMessage.InternalStatus = Message.BSMDStatus.SEND_FAILED;
systemError.MessageHeaderId = refMessage.Id;
DBManager.Instance.Save(refMessage);
MessageTelemetry.Dequeue(Message.NSWProvider.DUDR, refMessage);
}
}
else
{
if (systemError.Meldetype.Equals("REQUEST_ID"))
{
// Request gescheitert
Message refMessage = DBManager.Instance.GetMessage(aCore, aCore.IsTransit ? Message.NotificationClass.TRANSIT : Message.NotificationClass.VISIT);
if (refMessage != null)
{
refMessage.InternalStatus = Message.BSMDStatus.SEND_FAILED;
systemError.MessageHeaderId = refMessage.Id;
DBManager.Instance.Save(refMessage);
MessageTelemetry.Dequeue(Message.NSWProvider.DUDR, refMessage);
}
}
}
_log.WarnFormat("SystemError received for Core [{0}], IMO {1} ETA {2}: {3} Class {4}", aCore.Id, aCore.IMO, aCore.ETADisplay, systemError.ErrorMessage, Enum.GetName(typeof(Message.NotificationClass), notificationClass));
// Nach einiger Diskussion (vs. "SENT" und "FAILURE" geht das hier jetzt auf RESPONDED)
aCore.BSMDStatusInternal = MessageCore.BSMDStatus.RESPONDED;
DBManager.Instance.Save(aCore);
}
else
{
_log.WarnFormat("SystemError received for unknown core {0}: {1}", systemError.MessageCoreId, systemError.ErrorMessage);
}
// trotzdem immer speichern
DBManager.Instance.Save(systemError);
}
}
else
{
// NSW Rückmeldung
NSWResponse nswResponse = new NSWResponse(xml);
// Rückmeldung auswerten
if (nswResponse.MessageCoreId.HasValue)
{
MessageCore core = DBManager.Instance.GetMessageCoreById(nswResponse.MessageCoreId.Value);
if (core != null)
{
List<Message> messages = DBManager.Instance.GetMessagesForCore(core, DBManager.MessageLoad.ALL);
if (nswResponse.NotificationClass == Message.NotificationClass.VISIT)
{
if ((nswResponse.Status == "ACCEPTED") && !nswResponse.VisitId.IsNullOrEmpty())
{
_log.InfoFormat("Setting Visit-Id to {0} Core {1}", nswResponse.VisitId, core.Id);
core.VisitId = nswResponse.VisitId;
}
}
if (nswResponse.NotificationClass == Message.NotificationClass.TRANSIT)
{
if ((nswResponse.Status == "ACCEPTED") && !nswResponse.TransitId.IsNullOrEmpty())
{
_log.InfoFormat("Setting Transit-Id to {0} Core {1}", nswResponse.TransitId, core.Id);
core.TransitId = nswResponse.TransitId;
}
}
if (nswResponse.NotificationClass == Message.NotificationClass.STO)
{
_log.InfoFormat("NSWRESPONSE Cancel Visit/Transit Reply: {0} Cancel? {1}",
nswResponse.Status,
(core.Cancelled ?? false) ? "YES" : "NO"
);
if ((nswResponse.Status == "ACCEPTED") && (core.Cancelled ?? false))
{
core.BSMDStatusInternal = MessageCore.BSMDStatus.RESPONDED;
_log.InfoFormat("Core cancel confirmed for {0}", core.DisplayId);
}
if ((nswResponse.Status == "REJECTED") && (core.Cancelled ?? false))
{
core.Cancelled = false; // CANCEL fehlgeschlagen
core.BSMDStatusInternal = MessageCore.BSMDStatus.FAILURE;
_log.InfoFormat("Cancel rejected for {0}", core.DisplayId);
}
}
bool aMessageStillInSENTstate = false;
// now find the message that was meant..
foreach (Message aMessage in messages)
{
if (aMessage.MessageNotificationClass == nswResponse.NotificationClass)
{
if (nswResponse.Status != null)
{
aMessage.ReceivedAt = nswResponse.ReceiveAt;
bool isAccepted = (nswResponse.Status == "ACCEPTED");
if (isAccepted)
{
aMessage.SendSuccess = true;
aMessage.Status = Message.MessageStatus.ACCEPTED;
aMessage.InternalStatus = Message.BSMDStatus.CONFIRMED;
if (nswResponse.Violations.Count > 0)
{
aMessage.InternalStatus = Message.BSMDStatus.VIOLATION;
aMessage.StatusInfo = "Violations reported";
}
}
else
{
aMessage.Status = Message.MessageStatus.REJECTED;
aMessage.InternalStatus = Message.BSMDStatus.ERROR;
aMessage.StatusInfo = "Errors reported";
}
if (aMessage.Reset && nswResponse.IsReset && isAccepted)
{
_log.InfoFormat("Message {0} RESET confirmed", aMessage.Id);
aMessage.SendSuccess = false; // bestätigter RESET setzt den grünen Buppel wieder zurück
}
if (nswResponse.IsReset && !aMessage.Reset)
aMessage.Reset = nswResponse.IsReset;
}
else
{
_log.WarnFormat("Message status not found in ANSWER {0}", answerFile);
isOK = false;
}
#region Error / Violation handling
// "alte" Meldungen entfernen
foreach (MessageError existingError in aMessage.ErrorList)
DBManager.Instance.Delete(existingError);
foreach (MessageViolation existingViolation in aMessage.ViolationList)
DBManager.Instance.Delete(existingViolation);
if (!nswResponse.Violations.IsNullOrEmpty())
aMessage.InternalStatus = Message.BSMDStatus.VIOLATION;
foreach (MessageViolation messageViolation in nswResponse.Violations)
{
messageViolation.MessageHeaderId = aMessage.Id.Value;
messageViolation.MessageHeader = aMessage;
DBManager.Instance.Save(messageViolation);
}
if (!nswResponse.Errors.IsNullOrEmpty())
aMessage.InternalStatus = Message.BSMDStatus.ERROR;
foreach (MessageError messageError in nswResponse.Errors)
{
messageError.MessageHeaderId = aMessage.Id.Value;
messageError.MessageHeader = aMessage;
DBManager.Instance.Save(messageError);
}
_log.InfoFormat("{0} message {1} returned: Status {2} InternalStatus {3}",
aMessage.MessageNotificationClassDisplay, aMessage.Id, aMessage.Status, aMessage.InternalStatus);
DBManager.Instance.Save(aMessage);
MessageTelemetry.Dequeue(Message.NSWProvider.DUDR, aMessage);
#endregion
}
if (aMessage.InternalStatus == Message.BSMDStatus.SENT)
{
aMessageStillInSENTstate = true;
_log.DebugFormat("message {0} {1} still in SENT state", aMessage.Id, aMessage.MessageNotificationClassDisplay);
}
}
if (!aMessageStillInSENTstate && !(core.Cancelled ?? false))
core.BSMDStatusInternal = MessageCore.BSMDStatus.RESPONDED;
DBManager.Instance.Save(core);
}
else
{
_log.ErrorFormat("cannot find core for id {0}", nswResponse.MessageCoreId);
isOK = false;
}
}
else
{
_log.ErrorFormat("received response without suitable conveyance code, request id {0}", nswResponse.ClientRequestId);
isOK = false;
}
}
}
catch (Exception ex)
{
_log.WarnFormat("Exception deserializing ANSWER file: {0}", ex.ToString());
isOK = false;
}
}
}
}
if(isOK)
{
// archive file
string answerArchiveDir = Path.Combine(Properties.Settings.Default.TransmitterRoot, Properties.Settings.Default.AnswerArchiveDir);
string archiveFile = Path.Combine(answerArchiveDir, Path.GetFileName(answerFile));
try
{
if(File.Exists(archiveFile))
File.Delete(archiveFile);
File.Move(answerFile, archiveFile);
}
catch(Exception ex)
{
_log.ErrorFormat("cannot move {0} to {1}:{2}", answerFile, answerArchiveDir, ex.Message);
}
}
else
{
// save in separate folder (to look at it later?)
string answerCorruptDir = Path.Combine(Properties.Settings.Default.TransmitterRoot, Properties.Settings.Default.AnswerCorruptDir);
try
{
File.Move(answerFile, Path.Combine(answerCorruptDir, Path.GetFileName(answerFile)));
}
catch(Exception ex)
{
_log.ErrorFormat("cannot move {0} to {1}:{2}", answerFile, answerCorruptDir, ex.Message);
}
}
}
}
}
}