Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
Started adding an async message response helper to tackle #56
Browse files Browse the repository at this point in the history
  • Loading branch information
COM8 committed Mar 13, 2019
1 parent 4f70a25 commit 336b08b
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 9 deletions.
4 changes: 2 additions & 2 deletions Data_Manager2/Classes/ConnectionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,9 @@ private void onClientDisconneting(XMPPClient client)
/// <param name="client">The Client which entered the state.</param>
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));
Expand Down
24 changes: 17 additions & 7 deletions XMPP_API/Classes/GeneralCommandHelper.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -61,15 +63,23 @@ public async Task<string> setPreseceAsync(string from, string to, Presence prese
/// <summary>
/// Sends a RosterRequestMessage to the server and requests the current roster.
/// </summary>
/// <param name="onMessage">The method that should get executed once the helper receives a new valid message.</param>
/// <param name="onTimeout">The method that should get executed once the helper timeout gets triggered.</param>
/// <returns>Returns a MessageResponseHelper listening for RosterRequestMessage answers.</returns>
public MessageResponseHelper<IQMessage> requestRoster(MessageResponseHelper<IQMessage>.OnMessageHandler onMessage, MessageResponseHelper<IQMessage>.OnTimeoutHandler onTimeout)
/// <returns>The result of the request.</returns>
public async Task<MessageResponseHelperResult<IQMessage>> requestRosterAsync()
{
MessageResponseHelper<IQMessage> helper = new MessageResponseHelper<IQMessage>(CLIENT, onMessage, onTimeout);
Predicate<IQMessage> predicate = (x) => { return true; };
AsyncMessageResponseHelper<IQMessage> helper = new AsyncMessageResponseHelper<IQMessage>(CLIENT, predicate);
RosterRequestMessage msg = new RosterRequestMessage(CLIENT.getXMPPAccount().getFullJid(), CLIENT.getXMPPAccount().getBareJid());
helper.start(msg);
return helper;
return await helper.startAsync(msg);
}

/// <summary>
/// Sends a RosterRequestMessage to the server and requests the current roster.
/// </summary>
/// <returns>True if sending the message succeeded.</returns>
public async Task<bool> sendRequestRosterMessageAsync()
{
RosterRequestMessage msg = new RosterRequestMessage(CLIENT.getXMPPAccount().getFullJid(), CLIENT.getXMPPAccount().getBareJid());
return await CLIENT.sendAsync(msg);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace XMPP_API.Classes.Network.XML.Messages.Helper
{
public class AsyncMessageResponseHelper<T> : IDisposable where T : AbstractAddressableMessage
{
//--------------------------------------------------------Attributes:-----------------------------------------------------------------\\
#region --Attributes--
private readonly Predicate<T> IS_VALID_ANSWER;

private readonly IMessageSender MESSAGE_SENDER;
private readonly bool CACHE_IF_NOT_CONNECTED;
private readonly SemaphoreSlim METHOD_SEMA = new SemaphoreSlim(1, 1);

/// <summary>
/// Default timeout for requests is 5 seconds.
/// </summary>
public readonly TimeSpan TIMEOUT = TimeSpan.FromSeconds(5.0);
private Task timeoutTask;
private TaskCompletionSource<MessageResponseHelperResult<T>> completionSource;

public bool matchId = true;
public string sendId;
private bool done;

private bool disposed;

#endregion
//--------------------------------------------------------Constructor:----------------------------------------------------------------\\
#region --Constructors--
public AsyncMessageResponseHelper(IMessageSender messageSender, Predicate<T> isValidAnswer) : this(messageSender, isValidAnswer, false) { }

public AsyncMessageResponseHelper(IMessageSender messageSender, Predicate<T> 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<MessageResponseHelperResult<T>> 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<T>(MessageResponseHelperResultState.WILL_SEND_LATER);
}
return new MessageResponseHelperResult<T>(MessageResponseHelperResultState.SEND_FAILED);
}

if (disposed)
{
return new MessageResponseHelperResult<T>(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<T>(MessageResponseHelperResultState.TIMEOUT));
}

if (!(MESSAGE_SENDER is null))
{
MESSAGE_SENDER.NewValidMessage -= MESSAGE_SENDER_NewValidMessage;
}
}
}

#endregion

#region --Misc Methods (Private)--
private async Task<MessageResponseHelperResult<T>> waitForCompletionAsync()
{
// Create all tasks:
completionSource = new TaskCompletionSource<MessageResponseHelperResult<T>>();
timeoutTask = Task.Delay(TIMEOUT);

// Wait for completion:
Task resultTask = await Task.WhenAny(new Task[] { completionSource.Task, timeoutTask });
MessageResponseHelperResult<T> result = null;

// Evaluate and return result:
if (resultTask == completionSource.Task)
{
if (completionSource.Task.IsCompleted)
{
result = completionSource.Task.Result;
}
else
{
result = new MessageResponseHelperResult<T>(MessageResponseHelperResultState.ERROR);
}
}
else
{
result = new MessageResponseHelperResult<T>(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<T>(MessageResponseHelperResultState.SUCCESS, msg));
done = true;
}
}
METHOD_SEMA.Release();
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace XMPP_API.Classes.Network.XML.Messages.Helper
{
public class MessageResponseHelperResult<T> 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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace XMPP_API.Classes.Network.XML.Messages.Helper
{
public enum MessageResponseHelperResultState
{
/// <summary>
/// The request was successful.
/// </summary>
SUCCESS,
/// <summary>
/// A timeout occurred.
/// </summary>
TIMEOUT,
/// <summary>
/// The helper has been disposed.
/// </summary>
DISPOSED,
/// <summary>
/// An error occurred during sending the message.
/// </summary>
SEND_FAILED,
/// <summary>
/// A general error occurred.
/// </summary>
ERROR,
/// <summary>
/// Sending the message failed, but the message has been cached and will be send later.
/// </summary>
WILL_SEND_LATER
}
}
3 changes: 3 additions & 0 deletions XMPP_API/XMPP_API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
<Compile Include="Classes\Network\XML\Messages\AbstractAddressableMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\AbstractMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\AddToRosterMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\Helper\AsyncMessageResponseHelper.cs" />
<Compile Include="Classes\Network\XML\Messages\BindResourceMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\CloseStreamMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\Error.cs" />
Expand All @@ -198,12 +199,14 @@
<Compile Include="Classes\Network\XML\Messages\Features\TLSStreamFeature.cs" />
<Compile Include="Classes\Network\XML\Messages\Features\TLS\ProceedAnswerMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\Features\TLS\RequesStartTLSMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\Helper\MessageResponseHelperResult.cs" />
<Compile Include="Classes\Network\XML\Messages\IMessageSender.cs" />
<Compile Include="Classes\Network\XML\Messages\IQErrorMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\IQMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\IXElementable.cs" />
<Compile Include="Classes\Network\XML\Messages\MessageMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\MessageResponseHelper.cs" />
<Compile Include="Classes\Network\XML\Messages\Helper\MessageResponseHelperResultState.cs" />
<Compile Include="Classes\Network\XML\Messages\OpenStreamAnswerMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\OpenStreamMessage.cs" />
<Compile Include="Classes\Network\XML\Messages\PresenceErrorMessage.cs" />
Expand Down

0 comments on commit 336b08b

Please sign in to comment.