protected void Accepting()
{
Receive<ManagementCommand>(mc =>
{
/*
* applies a management command to all available transports.
*
* Useful for things like global restart
*/
var sender = Sender;
var allStatuses = _transportMapping.Values.Select(x => x.ManagementCommand(mc.Cmd));
Task.WhenAll(allStatuses)
.ContinueWith(x =>
{
return new ManagementCommandAck(x.Result.All(y => y));
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
.PipeTo(sender);
});
Receive<Quarantine>(quarantine =>
{
//Stop writers
var policy =
Tuple.Create(_endpoints.WritableEndpointWithPolicyFor(quarantine.RemoteAddress), quarantine.Uid);
if (policy.Item1 is Pass && policy.Item2 == null)
{
var endpoint = policy.Item1.AsInstanceOf<Pass>().Endpoint;
Context.Stop(endpoint);
_log.Warning("Association to [{0}] with unknown UID is reported as quarantined, but " +
"address cannot be quarantined without knowing the UID, gating instead for {1} ms.", quarantine.RemoteAddress, _settings.RetryGateClosedFor.TotalMilliseconds);
_endpoints.MarkAsFailed(endpoint, Deadline.Now + _settings.RetryGateClosedFor);
}
else if (policy.Item1 is Pass && policy.Item2 != null)
{
var pass = policy.Item1 as Pass;
if (pass.Uid == quarantine.Uid)
Context.Stop(pass.Endpoint);
}
else
{
// Do nothing, because either:
// A: we don't know yet the UID of the writer, it will be checked against current quarantine state later
// B: we know the UID, but it does not match with the UID to be quarantined
}
// Stop inbound read-only associations
var readPolicy = Tuple.Create(_endpoints.ReadOnlyEndpointFor(quarantine.RemoteAddress), quarantine.Uid);
if (readPolicy.Item1?.Item1 != null && quarantine.Uid == null)
Context.Stop(readPolicy.Item1.Item1);
else if (readPolicy.Item1?.Item1 != null && quarantine.Uid != null && readPolicy.Item1?.Item2 == quarantine.Uid) { Context.Stop(readPolicy.Item1.Item1); }
else { } // nothing to stop
Func<AkkaProtocolHandle, bool> matchesQuarantine = handle => handle.RemoteAddress.Equals(quarantine.RemoteAddress) &&
quarantine.Uid == handle.HandshakeInfo.Uid;
// Stop all matching pending read handoffs
_pendingReadHandoffs = _pendingReadHandoffs.Where(x =>
{
var drop = matchesQuarantine(x.Value);
// Side-effecting here
if (drop)
{
x.Value.Disassociate();
Context.Stop(x.Key);
}
return !drop;
}).ToDictionary(key => key.Key, value => value.Value);
// Stop all matching stashed connections
_stashedInbound = _stashedInbound.Select(x =>
{
var associations = x.Value.Where(assoc =>
{
var handle = assoc.Association.AsInstanceOf<AkkaProtocolHandle>();
var drop = matchesQuarantine(handle);
if (drop)
handle.Disassociate();
return !drop;
}).ToList();
return new KeyValuePair<IActorRef, List<InboundAssociation>>(x.Key, associations);
}).ToDictionary(k => k.Key, v => v.Value);
if (quarantine.Uid.HasValue)
{
_endpoints.MarkAsQuarantined(quarantine.RemoteAddress, quarantine.Uid.Value, Deadline.Now + _settings.QuarantineDuration);
_eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, quarantine.Uid.Value));
}
});
Receive<Send>(send =>
{
var recipientAddress = send.Recipient.Path.Address;
Func<int?, IActorRef> createAndRegisterWritingEndpoint = refuseUid => _endpoints.RegisterWritableEndpoint(recipientAddress,
CreateEndpoint(recipientAddress, send.Recipient.LocalAddressToUse,
_transportMapping[send.Recipient.LocalAddressToUse], _settings, writing: true,
handleOption: null, refuseUid: refuseUid), uid: null, refuseUid: refuseUid);
// pattern match won't throw a NullReferenceException if one is returned by WritableEndpointWithPolicyFor
_endpoints.WritableEndpointWithPolicyFor(recipientAddress).Match()
.With<Pass>(
pass =>
{
pass.Endpoint.Tell(send);
})
.With<Gated>(gated =>
{
if (gated.TimeOfRelease.IsOverdue) createAndRegisterWritingEndpoint(null).Tell(send);
else Context.System.DeadLetters.Tell(send);
})
.With<Quarantined>(quarantined =>
{
// timeOfRelease is only used for garbage collection reasons, therefore it is ignored here. We still have
// the Quarantined tombstone and we know what UID we don't want to accept, so use it.
createAndRegisterWritingEndpoint(quarantined.Uid).Tell(send);
})
.Default(msg => createAndRegisterWritingEndpoint(null).Tell(send));
});
Receive<InboundAssociation>(ia => HandleInboundAssociation(ia, false));
Receive<EndpointWriter.StoppedReading>(endpoint => AcceptPendingReader(endpoint.Writer));
Receive<Terminated>(terminated =>
{
AcceptPendingReader(terminated.ActorRef);
_endpoints.UnregisterEndpoint(terminated.ActorRef);
HandleStashedInbound(terminated.ActorRef, writerIsIdle: false);
});
Receive<EndpointWriter.TookOver>(tookover => RemovePendingReader(tookover.Writer, tookover.ProtocolHandle));
Receive<ReliableDeliverySupervisor.GotUid>(gotuid =>
{
_endpoints.RegisterWritableEndpointUid(gotuid.RemoteAddress, gotuid.Uid);
HandleStashedInbound(Sender, writerIsIdle: false);
});
Receive<ReliableDeliverySupervisor.Idle>(idle =>
{
HandleStashedInbound(Sender, writerIsIdle: true);
});
Receive<Prune>(prune => _endpoints.Prune());
Receive<ShutdownAndFlush>(shutdown =>
{
//Shutdown all endpoints and signal to Sender when ready (and whether all endpoints were shutdown gracefully)
var sender = Sender;
// The construction of the Task for shutdownStatus has to happen after the flushStatus future has been finished
// so that endpoints are shut down before transports.
var shutdownStatus = Task.WhenAll(_endpoints.AllEndpoints.Select(
x => x.GracefulStop(_settings.FlushWait, EndpointWriter.FlushAndStop.Instance))).ContinueWith(
result =>
{
if (result.IsFaulted || result.IsCanceled)
{
if (result.Exception != null)
result.Exception.Handle(e => true);
return false;
}
return result.Result.All(x => x);
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
shutdownStatus.ContinueWith(tr => Task.WhenAll(_transportMapping.Values.Select(x => x.Shutdown())).ContinueWith(
result =>
{
if (result.IsFaulted || result.IsCanceled)
{
if (result.Exception != null)
result.Exception.Handle(e => true);
return false;
}
return result.Result.All(x => x) && tr.Result;
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)).Unwrap().PipeTo(sender);
foreach (var handoff in _pendingReadHandoffs.Values)
{
handoff.Disassociate(DisassociateInfo.Shutdown);
}
//Ignore all other writes
_normalShutdown = true;
Become(Flushing);
});
}