Created
May 14, 2015 01:37
-
-
Save ReubenBond/a16f7ace984eadf98b75 to your computer and use it in GitHub Desktop.
ChatRoomGrain snippet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"Don't expect this to compile - it's just a snippet" | |
/// <summary> | |
/// The chat room grain. | |
/// </summary> | |
[StorageProvider(ProviderName = "chats")] | |
[Reentrant] | |
public class ChatRoomGrain : Grain<IState<ChatRoom>>, IChatRoomGrain | |
{ | |
/// <summary> | |
/// The address. | |
/// </summary> | |
private Address address; | |
/// <summary> | |
/// The log. | |
/// </summary> | |
private Logger log; | |
/// <summary> | |
/// Activate this instance. | |
/// </summary> | |
/// <returns> | |
/// A <see cref="Task"/> representing the work performed. | |
/// </returns> | |
public override Task OnActivateAsync() | |
{ | |
this.address = new Address { Kind = Kinds.ChatRoom, Id = this.GetPrimaryKey() }; | |
this.log = (Logger)this.GetLogger(this.address.ToString()); | |
this.State.Value.Messages = this.State.Value.Messages ?? new List<ChatMessage>(); | |
this.State.Value.Participants = this.State.Value.Participants ?? new HashSet<Address>(); | |
return base.OnActivateAsync(); | |
} | |
/// <summary> | |
/// Closes this room. | |
/// </summary> | |
/// <param name="user">The caller.</param> | |
/// <returns>A <see cref="Task"/> representing the work performed.</returns> | |
public async Task Close(IUserGrain user) | |
{ | |
this.ThrowIfNotInitialized(); | |
await this.ThrowIfNotMemberOrAdmin(user); | |
this.State.Value.State = ChatRoomState.Closed; | |
this.log.Info("Closed."); | |
await this.State.WriteStateAsync(); | |
} | |
/// <summary> | |
/// Returns the actor state. | |
/// </summary> | |
/// <param name="user"> | |
/// The requesting user. | |
/// </param> | |
/// <returns> | |
/// The actor state. | |
/// </returns> | |
public async Task<ChatRoom> Get(IUserGrain user) | |
{ | |
await this.ThrowIfNotMemberOrAdmin(user); | |
return this.State.Value; | |
} | |
/// <summary> | |
/// Sends the provided <paramref name="message"/> to the participants in this room. | |
/// </summary> | |
/// <param name="user">The user.</param> | |
/// <param name="message">The message.</param> | |
/// <returns>A <see cref="Task"/> representing the work performed.</returns> | |
public async Task SendMessage(IUserGrain user, string message) | |
{ | |
this.ThrowIfNotInitialized(); | |
this.ThrowIfClosed(); | |
await this.ThrowIfNotMemberOrAdmin(user); | |
if (string.IsNullOrWhiteSpace(message)) | |
{ | |
return; | |
} | |
var now = DateTime.UtcNow; | |
var msg = new ChatMessage { Room = this.address, Time = now, Message = message, Sender = user.GetAddress() }; | |
var recipients = this.State.Value.Participants; | |
this.State.Value.Messages.Add(msg); | |
await this.State.WriteStateAsync(); | |
this.log.Info("Message: {0} {1}", user.GetAddress(), message); | |
foreach (var actor in recipients.Select(recipient => GrainFactory.GetGrain<IUserGrain>(recipient.Id))) | |
{ | |
await actor.NewMessage(msg); | |
} | |
} | |
/// <summary> | |
/// Returns a value indicating whether this room was created. | |
/// </summary> | |
/// <param name="participants">The room participants.</param> | |
/// <param name="item">The item.</param> | |
/// <returns>A value indicating whether this room was created.</returns> | |
public async Task<bool> Create(HashSet<Address> participants, Item item) | |
{ | |
if (this.State.Value.State != ChatRoomState.Uninitialized) | |
{ | |
return false; | |
} | |
this.log.Info("Create({0}, {1})", string.Join(", ", participants), item); | |
this.State.Value.Participants = participants; | |
this.State.Value.State = ChatRoomState.Created; | |
this.State.Value.Item = item; | |
await this.State.WriteStateAsync(); | |
return true; | |
} | |
/// <summary> | |
/// Removes persisted state for this instance. | |
/// </summary> | |
/// <param name="admin"> | |
/// The administrator. | |
/// </param> | |
/// <returns> | |
/// A <see cref="Task"/> representing the work performed. | |
/// </returns> | |
public async Task RemoveState(IUserGrain admin) | |
{ | |
this.log.Info("RemoveState({0})", admin.GetAddress()); | |
await admin.ThrowIfNotAdmin(); | |
await this.State.ClearStateAsync(); | |
this.DeactivateOnIdle(); | |
} | |
/// <summary> | |
/// The deactivate. | |
/// </summary> | |
/// <param name="admin"> | |
/// The admin. | |
/// </param> | |
/// <returns> | |
/// A <see cref="Task"/> representing the work performed. | |
/// </returns> | |
public async Task Deactivate(IUserGrain admin) | |
{ | |
this.log.Info("Deactivate({0})", admin.GetAddress()); | |
await admin.ThrowIfNotAdmin(); | |
this.DeactivateOnIdle(); | |
} | |
/// <summary> | |
/// Attempts to repair this instance. | |
/// </summary> | |
/// <param name="admin">The caller.</param> | |
/// <returns>A <see cref="Task"/> representing the work performed.</returns> | |
public Task Repair(IUserGrain admin) | |
{ | |
this.log.Info("Repair - no repair actions to take."); | |
return Task.FromResult(0); | |
} | |
/// <summary> | |
/// Handles grain deactivation. | |
/// </summary> | |
/// <returns>A <see cref="Task"/> representing the work performed.</returns> | |
public override Task OnDeactivateAsync() | |
{ | |
this.log.Info("Deactivating."); | |
return base.OnDeactivateAsync(); | |
} | |
/// <summary> | |
/// Sends a reminder to this instance. | |
/// </summary> | |
/// <param name="admin">The admin.</param> | |
/// <param name="reminderName">The reminder name.</param> | |
/// <returns>A <see cref="Task"/> representing the work performed.</returns> | |
public async Task Reminder(IUserGrain admin, string reminderName) | |
{ | |
await admin.ThrowIfNotAdmin(); | |
await this.ReceiveReminder(reminderName, new TickStatus()); | |
} | |
/// <summary> | |
/// Handles a reminder. | |
/// </summary> | |
/// <param name="reminderName"> | |
/// The reminder name. | |
/// </param> | |
/// <param name="tickStatus"> | |
/// The tick status. | |
/// </param> | |
/// <returns> | |
/// A <see cref="Task"/> representing the work performed. | |
/// </returns> | |
public Task ReceiveReminder(string reminderName, TickStatus tickStatus) | |
{ | |
return Task.FromResult(0); | |
} | |
/// <summary> | |
/// Throws an exception if the provided <paramref name="user"/> is not a participant. | |
/// </summary> | |
/// <param name="user"> | |
/// The user. | |
/// </param> | |
/// <returns> | |
/// A <see cref="Task"/> representing the work performed. | |
/// </returns> | |
private async Task ThrowIfNotMemberOrAdmin(IUserGrain user) | |
{ | |
var userAddress = user.GetAddress(); | |
if (this.State.Value.Participants != null && !this.State.Value.Participants.Contains(userAddress)) | |
{ | |
if (await user.IsAdmin()) | |
{ | |
return; | |
} | |
this.log.Warn("{0} is not a participant.", new object[] { userAddress }); | |
throw ExceptionUtil.Create( | |
ErrorCode.AccessDenied, | |
"Sender (" + userAddress + ") is not a participant.", | |
log: this.log); | |
} | |
} | |
/// <summary> | |
/// Throws an exception if the room is not initialized. | |
/// </summary> | |
/// <exception cref="InvalidOperationException"> | |
/// The room is not initialized. | |
/// </exception> | |
private void ThrowIfNotInitialized() | |
{ | |
if (this.State.Value.State == ChatRoomState.Uninitialized) | |
{ | |
throw ExceptionUtil.Create(ErrorCode.Invalid, "Instance not yet initialized.", log: this.log); | |
} | |
} | |
/// <summary> | |
/// Throws an exception if the room is closed.. | |
/// </summary> | |
private void ThrowIfClosed() | |
{ | |
if (this.State.Value.State == ChatRoomState.Closed) | |
{ | |
throw ExceptionUtil.Create(ErrorCode.Invalid, "Cannot send message to closed room.", log: this.log); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment