diff --git a/Data_Manager2/Classes/ConnectionHandler.cs b/Data_Manager2/Classes/ConnectionHandler.cs index b96c9e7e7..36498196f 100644 --- a/Data_Manager2/Classes/ConnectionHandler.cs +++ b/Data_Manager2/Classes/ConnectionHandler.cs @@ -276,9 +276,9 @@ private void onClientDisconneting(XMPPClient client) /// The Client which entered the state. private void onClientConnected(XMPPClient client) { - Task.Run(() => + Task.Run(async () => { - client.GENERAL_COMMAND_HELPER.requestRoster(null, null); + await client.GENERAL_COMMAND_HELPER.sendRequestRosterMessageAsync(); client.PUB_SUB_COMMAND_HELPER.requestBookmars_xep_0048(null, null); MUCHandler.INSTANCE.onClientConnected(client); ClientConnected?.Invoke(this, new ClientConnectedEventArgs(client)); diff --git a/XMPP_API/Classes/GeneralCommandHelper.cs b/XMPP_API/Classes/GeneralCommandHelper.cs index c55588e3e..c4f27abad 100644 --- a/XMPP_API/Classes/GeneralCommandHelper.cs +++ b/XMPP_API/Classes/GeneralCommandHelper.cs @@ -1,6 +1,8 @@ using Logging; +using System; using System.Threading.Tasks; using XMPP_API.Classes.Network.XML.Messages; +using XMPP_API.Classes.Network.XML.Messages.Helper; using XMPP_API.Classes.Network.XML.Messages.XEP_0030; using XMPP_API.Classes.Network.XML.Messages.XEP_0085; @@ -61,15 +63,23 @@ public async Task setPreseceAsync(string from, string to, Presence prese /// /// Sends a RosterRequestMessage to the server and requests the current roster. /// - /// The method that should get executed once the helper receives a new valid message. - /// The method that should get executed once the helper timeout gets triggered. - /// Returns a MessageResponseHelper listening for RosterRequestMessage answers. - public MessageResponseHelper requestRoster(MessageResponseHelper.OnMessageHandler onMessage, MessageResponseHelper.OnTimeoutHandler onTimeout) + /// The result of the request. + public async Task> requestRosterAsync() { - MessageResponseHelper helper = new MessageResponseHelper(CLIENT, onMessage, onTimeout); + Predicate predicate = (x) => { return true; }; + AsyncMessageResponseHelper helper = new AsyncMessageResponseHelper(CLIENT, predicate); RosterRequestMessage msg = new RosterRequestMessage(CLIENT.getXMPPAccount().getFullJid(), CLIENT.getXMPPAccount().getBareJid()); - helper.start(msg); - return helper; + return await helper.startAsync(msg); + } + + /// + /// Sends a RosterRequestMessage to the server and requests the current roster. + /// + /// True if sending the message succeeded. + public async Task sendRequestRosterMessageAsync() + { + RosterRequestMessage msg = new RosterRequestMessage(CLIENT.getXMPPAccount().getFullJid(), CLIENT.getXMPPAccount().getBareJid()); + return await CLIENT.sendAsync(msg); } /// diff --git a/XMPP_API/Classes/Network/XML/Messages/Helper/AsyncMessageResponseHelper.cs b/XMPP_API/Classes/Network/XML/Messages/Helper/AsyncMessageResponseHelper.cs new file mode 100644 index 000000000..59ea81dd9 --- /dev/null +++ b/XMPP_API/Classes/Network/XML/Messages/Helper/AsyncMessageResponseHelper.cs @@ -0,0 +1,164 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace XMPP_API.Classes.Network.XML.Messages.Helper +{ + public class AsyncMessageResponseHelper : IDisposable where T : AbstractAddressableMessage + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + private readonly Predicate IS_VALID_ANSWER; + + private readonly IMessageSender MESSAGE_SENDER; + private readonly bool CACHE_IF_NOT_CONNECTED; + private readonly SemaphoreSlim METHOD_SEMA = new SemaphoreSlim(1, 1); + + /// + /// Default timeout for requests is 5 seconds. + /// + public readonly TimeSpan TIMEOUT = TimeSpan.FromSeconds(5.0); + private Task timeoutTask; + private TaskCompletionSource> completionSource; + + public bool matchId = true; + public string sendId; + private bool done; + + private bool disposed; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public AsyncMessageResponseHelper(IMessageSender messageSender, Predicate isValidAnswer) : this(messageSender, isValidAnswer, false) { } + + public AsyncMessageResponseHelper(IMessageSender messageSender, Predicate isValidAnswer, bool cacheIfNotConnected) + { + this.MESSAGE_SENDER = messageSender; + this.IS_VALID_ANSWER = isValidAnswer; + this.CACHE_IF_NOT_CONNECTED = cacheIfNotConnected; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public void Dispose() + { + stop(); + disposed = true; + } + + public async Task> startAsync(AbstractMessage msg) + { + done = false; + MESSAGE_SENDER.NewValidMessage += MESSAGE_SENDER_NewValidMessage; + + bool success = await MESSAGE_SENDER.sendAsync(msg, CACHE_IF_NOT_CONNECTED); + if (!success) + { + if (CACHE_IF_NOT_CONNECTED) + { + return new MessageResponseHelperResult(MessageResponseHelperResultState.WILL_SEND_LATER); + } + return new MessageResponseHelperResult(MessageResponseHelperResultState.SEND_FAILED); + } + + if (disposed) + { + return new MessageResponseHelperResult(MessageResponseHelperResultState.DISPOSED); + } + + return await waitForCompletionAsync(); + } + + public void stop() + { + if (!disposed) + { + done = true; + if (!(completionSource is null) && !completionSource.Task.IsCanceled && !completionSource.Task.IsCompleted && !completionSource.Task.IsFaulted) + { + completionSource.SetResult(new MessageResponseHelperResult(MessageResponseHelperResultState.TIMEOUT)); + } + + if (!(MESSAGE_SENDER is null)) + { + MESSAGE_SENDER.NewValidMessage -= MESSAGE_SENDER_NewValidMessage; + } + } + } + + #endregion + + #region --Misc Methods (Private)-- + private async Task> waitForCompletionAsync() + { + // Create all tasks: + completionSource = new TaskCompletionSource>(); + timeoutTask = Task.Delay(TIMEOUT); + + // Wait for completion: + Task resultTask = await Task.WhenAny(new Task[] { completionSource.Task, timeoutTask }); + MessageResponseHelperResult result = null; + + // Evaluate and return result: + if (resultTask == completionSource.Task) + { + if (completionSource.Task.IsCompleted) + { + result = completionSource.Task.Result; + } + else + { + result = new MessageResponseHelperResult(MessageResponseHelperResultState.ERROR); + } + } + else + { + result = new MessageResponseHelperResult(MessageResponseHelperResultState.TIMEOUT); + } + + stop(); + return result; + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private async void MESSAGE_SENDER_NewValidMessage(IMessageSender sender, Events.NewValidMessageEventArgs args) + { + await METHOD_SEMA.WaitAsync(); + if (disposed || done) + { + return; + } + + if (args.MESSAGE is T msg) + { + if (matchId && !string.Equals(sendId, msg.ID)) + { + return; + } + + if (IS_VALID_ANSWER(msg)) + { + completionSource.TrySetResult(new MessageResponseHelperResult(MessageResponseHelperResultState.SUCCESS, msg)); + done = true; + } + } + METHOD_SEMA.Release(); + } + + #endregion + } +} diff --git a/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResult.cs b/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResult.cs new file mode 100644 index 000000000..3f7b3e715 --- /dev/null +++ b/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResult.cs @@ -0,0 +1,48 @@ +namespace XMPP_API.Classes.Network.XML.Messages.Helper +{ + public class MessageResponseHelperResult where T : AbstractAddressableMessage + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly MessageResponseHelperResultState STATE; + public readonly T RESULT; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + internal MessageResponseHelperResult(MessageResponseHelperResultState state) : this(state, null) { } + + internal MessageResponseHelperResult(MessageResponseHelperResultState state, T result) + { + this.STATE = state; + this.RESULT = result; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResultState.cs b/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResultState.cs new file mode 100644 index 000000000..5b8d2e23a --- /dev/null +++ b/XMPP_API/Classes/Network/XML/Messages/Helper/MessageResponseHelperResultState.cs @@ -0,0 +1,30 @@ +namespace XMPP_API.Classes.Network.XML.Messages.Helper +{ + public enum MessageResponseHelperResultState + { + /// + /// The request was successful. + /// + SUCCESS, + /// + /// A timeout occurred. + /// + TIMEOUT, + /// + /// The helper has been disposed. + /// + DISPOSED, + /// + /// An error occurred during sending the message. + /// + SEND_FAILED, + /// + /// A general error occurred. + /// + ERROR, + /// + /// Sending the message failed, but the message has been cached and will be send later. + /// + WILL_SEND_LATER + } +} diff --git a/XMPP_API/XMPP_API.csproj b/XMPP_API/XMPP_API.csproj index af422c795..79547b17d 100644 --- a/XMPP_API/XMPP_API.csproj +++ b/XMPP_API/XMPP_API.csproj @@ -176,6 +176,7 @@ + @@ -198,12 +199,14 @@ + +