/// <summary> /// This method creates a service message and injects it in to the execution path and bypasses the listener infrastructure. /// </summary> /// <param name="header">The message header to identify the recipient.</param> /// <param name="package">The objet package to process.</param> /// <param name="ChannelPriority">The prioirty that the message should be processed. The default is 1. If this message is not a valid value, it will be matched to the nearest valid value.</param> /// <param name="options">The process options.</param> /// <param name="release">The release action which is called when the payload has been executed.</param> /// <param name="isDeadLetterMessage">A flag indicating whether the message is a deadletter replay. These messages may be treated differently /// by the receiving commands.</param> public void Process(ServiceMessageHeader header , object package = null , int ChannelPriority = 1 , ProcessOptions options = ProcessOptions.RouteExternal | ProcessOptions.RouteInternal , Action <bool, Guid> release = null , bool isDeadLetterMessage = false) { var message = new ServiceMessage(header); message.ChannelPriority = ChannelPriority; if (package != null) { message.Blob = mSerializer.PayloadSerialize(package); } Process(message, options, release, isDeadLetterMessage); }
/// <summary> /// This method attaches a rewrite rule to the channel pipeline. /// </summary> /// <typeparam name="C">The channel pipeline type.</typeparam> /// <param name="cpipe">The incoming pipeline.</param> /// <param name="canRedirect">The match function.</param> /// <param name="changeHeader">The redirect header.</param> /// <param name="canCache">Specifies whether the redirect hit can be cached.</param> /// <returns>Returns the original pipeline.</returns> public static C AttachMessageRedirectRule <C>(this C cpipe , Func <TransmissionPayload, bool> canRedirect , ServiceMessageHeader changeHeader , bool canCache = true) where C : IPipelineChannel <IPipeline> { Action <TransmissionPayload> updateHeader = (p) => { p.Message.ChannelId = changeHeader.ChannelId; p.Message.MessageType = changeHeader.MessageType; p.Message.ActionType = changeHeader.ActionType; }; cpipe.AttachMessageRedirectRule(canRedirect, updateHeader, canCache); return(cpipe); }
/// <summary> /// This method replaces the channel with the command default if the value specified in the attribute is null. /// </summary> /// <param name="attr">The incoming attribute whose header channel should be checked.</param> /// <returns>Returns a message filter wrapper for the header.</returns> protected MessageFilterWrapper CommandChannelIdAdjust <A>(A attr) where A : CommandContractAttributeBase { ServiceMessageHeader header = attr.Header; if (header.ChannelId == null) { if (ChannelId == null) { throw new CommandChannelIdNullException($"Command '{FriendlyName}' uses CommandContracts without a Channel id set but its default ChannelId is null."); } header = new ServiceMessageHeader(ChannelId, header.MessageType, header.ActionType); } return(new MessageFilterWrapper(header)); }
/// <summary> /// This method is used to send requests to a remote command and wait for a response. /// </summary> /// <typeparam name="RQ">The request object type.</typeparam> /// <typeparam name="RS">The response object type.</typeparam> /// <param name="header">The message header object that defines the remote endpoint.</param> /// <param name="rq">The request object.</param> /// <param name="settings">The request settings. Use this to specifically set the timeout parameters, amongst other settings, or to pass meta data to the calling party.</param> /// <param name="routing">The routing options by default this will try internal and then external endpoints.</param> /// <param name="principal">This is the principal that you wish the command to be executed under. /// By default this is taken from the calling thread if not passed.</param> /// <returns>Returns the async response wrapper.</returns> public virtual async Task <ResponseWrapper <RS> > Process <RQ, RS>( ServiceMessageHeader header , RQ rq , RequestSettings settings = null , ProcessOptions?routing = default(ProcessOptions?) , IPrincipal principal = null ) { ServiceStartedCheck(); return(await ProcessOutgoing <RQ, RS>( header.ChannelId, header.MessageType, header.ActionType , rq , settings , routing , fallbackMaxProcessingTime : mDefaultRequestTimespan , principal : principal ?? Thread.CurrentPrincipal)); }
/// <summary> /// Intercepts the specified event arguments. /// </summary> /// <typeparam name="H">The harness type.</typeparam> /// <param name="harness">The harness.</param> /// <param name="action">The event arguments action.</param> /// <param name="direction">The optional direction filter.</param> /// <param name="header">The optional header.</param> /// <param name="processResponses">Set this to true to send any responses passed back in the context to the harness dispatcher. The default is true.</param> /// <returns>The harness</returns> public static H Intercept <H>(this H harness , Action <CommandHarnessRequestContext> action , CommandHarnessTrafficDirection?direction = null , ServiceMessageHeader header = null , bool processResponses = true) where H : ICommandHarness { if (action == null) { throw new ArgumentNullException("action", "action cannot be null"); } harness.OnEvent += (object sender, CommandHarnessEventArgs e) => { //Is this a payload event, no then do not bubble up. if (!e.Event.Tracker.HasTransmissionPayload()) { return; } if (direction.HasValue && e.Event.Direction != direction.Value) { return; } var payload = e.Event.Tracker.ToTransmissionPayload(); ServiceMessageHeader headerFind = payload.Message.ToServiceMessageHeader(); if (header == null || header.IsMatch(headerFind)) { var context = new CommandHarnessRequestContext(harness, e, payload); action(context); if (processResponses) { context.Responses.ForEach((r) => harness.Dispatcher.Process(r)); } } }; return(harness); }
/// <summary> /// This method creates a service message and injects it in to the execution path and bypasses the listener infrastructure. /// </summary> /// <param name="header">The message header fragment to identify the recipient. The channel will be inserted by the command harness.</param> /// <param name="package">The object package to process.</param> /// <param name="ChannelPriority">The priority that the message should be processed. The default is 1. If this message is not a valid value, it will be matched to the nearest valid value.</param> /// <param name="options">The process options.</param> /// <param name="release">The release action which is called when the payload has been executed by the receiving commands.</param> /// <param name="responseHeader">This is the optional response header</param> /// <param name="ResponseChannelPriority">This is the response channel priority. This will be set if the response header is not null. The default priority is 1.</param> /// <param name="originatorServiceId">This optional parameter allows you to set the originator serviceId</param> public void Process(ServiceMessageHeaderFragment header , object package = null , int ChannelPriority = 1 , ProcessOptions options = ProcessOptions.RouteExternal | ProcessOptions.RouteInternal , Action <bool, Guid> release = null , ServiceMessageHeader responseHeader = null , int ResponseChannelPriority = 1 , string originatorServiceId = null ) { Process((Policy.ChannelId, header) , package , ChannelPriority , options , release , responseHeader , ResponseChannelPriority , originatorServiceId ); }
/// <summary> /// This method returns a list of SqlFilter object for the listener channel. /// </summary> /// <returns>The list of SqlFilter object</returns> protected virtual List <SqlFilter> GetFilters() { List <SqlFilter> list; string id = OriginatorId.ExternalServiceId; if (id.Length > 50) { id = id.Substring(0, 50); } if (ListenOnOriginatorId) { list = new List <SqlFilter>(); var servMess = new ServiceMessageHeader(ChannelId); list.Add(TopicHelper.SqlFilterQuery(new MessageFilterWrapper(servMess, OriginatorId.ExternalServiceId))); } else { list = TopicHelper.SqlFilter(mSupportedMessageTypes, ChannelId, ListenerMappingChannelId); } return(list); }
protected virtual bool SupportedResolve(ServiceMessageHeader header, out H command) { foreach (var item in mSupported) { if (item.Key.Message.Header.IsPartialKey) { string partialkey = item.Key.Message.Header.ToPartialKey(); if (header.ToKey().StartsWith(partialkey)) { command = item.Value; return(true); } } else if (item.Key.Message.Header.Equals(header)) { command = item.Value; return(true); } } command = null; return(false); }
/// <summary> /// Attaches the UDP listener to the incoming channel. /// </summary> /// <typeparam name="C">The pipeline type.</typeparam> /// <param name="cpipe">The pipeline.</param> /// <param name="udp">The UDP endpoint configuration.</param> /// <param name="defaultDeserializerContentType">Default deserializer MIME Content-type, i.e application/json.</param> /// <param name="defaultDeserializerContentEncoding">Default deserializer MIME Content-encoding, i.e. GZIP.</param> /// <param name="requestAddress">This is the optional address fragment which specifies the incoming message destination. If this is not set then ("","") will be used. This does not include a channelId as this will be provided by the pipeline.</param> /// <param name="responseAddress">This is the optional return address destination to be set for the incoming messages.</param> /// <param name="requestAddressPriority">This is the default priority for the request message. The default is null. This will inherit from the channel priority.</param> /// <param name="responseAddressPriority">This is the priority for the response address. The default is 1.</param> /// <param name="deserialize">The deserialize action.</param> /// <param name="canDeserialize">The deserialize check function.</param> /// <param name="action">The optional action to be called when the listener is created.</param> /// <returns>Returns the pipeline.</returns> public static C AttachUdpListener <C>(this C cpipe , UdpConfig udp , string defaultDeserializerContentType = null , string defaultDeserializerContentEncoding = null , ServiceMessageHeaderFragment requestAddress = null , ServiceMessageHeader responseAddress = null , int?requestAddressPriority = null , int responseAddressPriority = 1 , Action <SerializationHolder> deserialize = null , Func <SerializationHolder, bool> canDeserialize = null , Action <UdpChannelListener> action = null ) where C : IPipelineChannelIncoming <IPipeline> { defaultDeserializerContentType = ( defaultDeserializerContentType ?? $"udp_in/{cpipe.Channel.Id}" ).ToLowerInvariant(); var listener = new UdpChannelListener(udp , defaultDeserializerContentType, defaultDeserializerContentEncoding , requestAddress, responseAddress, requestAddressPriority, responseAddressPriority ); if (deserialize != null) { cpipe.Pipeline.AddPayloadSerializer( defaultDeserializerContentType , deserialize: deserialize , canDeserialize: canDeserialize); } cpipe.AttachListener(listener, action, true); return(cpipe); }
/// <summary> /// This method converts the message metadata in to a header. /// </summary> /// <returns></returns> public virtual string ToKey() { return(ServiceMessageHeader.ToKey(ChannelId, MessageType, ActionType)); }
/// <summary> /// This is the constructor for registering a manual command. /// </summary> /// <param name="header">The ServiceMessageHeader route for the command.</param> /// <param name="command">The command to process.</param> /// <param name="referenceId">The optional reference id for tracking.</param> public CommandInline(ServiceMessageHeader header , Func <TransmissionPayload, List <TransmissionPayload>, IPayloadSerializationContainer, Task> command , string referenceId = null) : this(new MessageFilterWrapper(header), command, referenceId) { }
/// <summary> /// This method unregisters a particular command. /// </summary> /// <param name="header"></param> /// <param name="isMasterJob">Specifies whether the command is linked to a master job.</param> protected void CommandUnregister(ServiceMessageHeader header, bool isMasterJob = false) { CommandUnregister(new MessageFilterWrapper(header, null), isMasterJob); }
public CommandContractAttribute(string messageType, string actionType) { Header = new ServiceMessageHeader(null, messageType, actionType); }
public CommandContractAttribute(string channelId = null, string messageType = null, string actionType = null) { Header = new ServiceMessageHeader(channelId, messageType, actionType); }
/// <summary> /// Intercepts just the outgoing messages, and then processes any responses returned in the context. /// </summary> /// <typeparam name="H">The harness type.</typeparam> /// <param name="harness">The harness.</param> /// <param name="action">The action.</param> /// <param name="header">The optional message destination header filter. You can pass a partial header.</param> /// <returns>Returns the harness to continue the pipeline.</returns> /// <exception cref="ArgumentNullException">action - action cannot be null</exception> public static H InterceptOutgoing <H>(this H harness, Action <CommandHarnessRequestContext> action, ServiceMessageHeader header = null) where H : ICommandHarness { return(harness.Intercept(action, CommandHarnessTrafficDirection.Outgoing, header, true)); }
/// <summary> /// This method converts the message response metadata in to a header. /// </summary> /// <returns></returns> public virtual string ToResponseKey() { return(ServiceMessageHeader.ToKey(ResponseChannelId, ResponseMessageType, ResponseActionType)); }
/// <summary> /// This is the default constructor. /// </summary> /// <param name="header">THe actual service message header.</param> /// <param name="clientId">This is the optional client id.</param> public MessageFilterWrapper(ServiceMessageHeader header, string clientId = null) { Header = header; ClientId = clientId; }
/// <summary> /// This is the default /// </summary> /// <param name="channelId">The channelId.</param> /// <param name="messageType">The message type.</param> /// <param name="messageAction">The action type.</param> public ContractAttribute(string channelId, string messageType, string messageAction) { mHeader = new ServiceMessageHeader(channelId, messageType, messageAction); }
/// <summary> /// This constructor sets the header parameters. /// </summary> /// <param name="header">The header</param> public ServiceMessage(ServiceMessageHeader header) : this() { ChannelId = header.ChannelId; MessageType = header.MessageType; ActionType = header.ActionType; }
/// <summary> /// This exception is thrown when a request is sent to a command that is not recognised. /// </summary> /// <param name="id">The payload id.</param> /// <param name="header">The message header.</param> /// <param name="commandType">The command type.</param> public CommandNotSupportedException(Guid id, ServiceMessageHeader header, Type commandType) : base($"The command '{header.Key}' is not supported in {commandType.Name}") { Id = id; Header = header; Command = commandType.Name; }
public MessageFilterWrapper(ServiceMessageHeader header, bool isDeadLetter = false) { Header = header; IsDeadLetter = isDeadLetter; }
/// <summary> /// This is the default constructor. /// </summary> /// <param name="header">THe actual service message header.</param> public MessageFilterWrapper(ServiceMessageHeader header) { Header = header; }
/// <summary> /// This commands returns true is the command channelId and action are supported. /// </summary> /// <param name="header">The message header.</param> /// <returns>Returns true if the message is supported.</returns> public virtual bool SupportsMessage(ServiceMessageHeader header) { H command; return(SupportedResolve(header, out command)); }
/// <summary> /// Initializes a new instance of the <see cref="CommandContractAttributeBase"/> class. /// </summary> /// <param name="header">The service message header.</param> protected CommandContractAttributeBase(ServiceMessageHeader header) { Header = header; }