śĪęCŻōQ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPNone+Heartbeat controller typeError Handler:Q$: Exception that led the invocation;6String: Name of the entity (client name, server name, etc.)Patterns ExceptionApplication-defined exception+No provider for the requested job available9Heartbeat proposed by registry out of acceptable rangeJThrown on missing heartbeat (after tolerance of 10 missing heartbeats).Error on Header identified by the first StringStatus code other than OK Invalid status code Timout expired Name of a Stomp queue  Name of a service, task or topic „Status code to communicate the state of a request between two applications. The wire format is inspired by HTTP status codes:OK (200): Everything is fine5BadRequest (400): Syntax error in the request messageForbidden (403): Not used>NotFound (404): For the requested job no provider is availableTimeout (408): Timeout expiredSafe StatusCode parser (  is instance of R , but S/ would cause an error on an invalid StatusCode)*OutBound converter for messages of type ())InBound converter for messages of type ()(OutBound converter for messages of type T'InBound converter for messages of type TICreate a heartbeat controller; receives the heartbeat in milliseconds.(UVW XYZ[\]^_`abc%UVW XYZ[\]^_`UVW  XYZ[\]^_`abcNoneRegistry: An opaque data typeĆA provider is an opaque data type; most of its attributes are used only internally by the registry. Interesting for user applications, however, is the queue that identifies the provider.(Queue through which the job is provided qA helper that shall ease the use of the registers. A registry to which a call wants to connect is described asThe  . through which the registry receives requests;The  in microseconds, i.e.K the time the caller will wait before the request fails;/A triple of heartbeat specifications: the best value, i.e.b the rate at which the caller prefers to send heartbeats, the minimumV rate at which the caller can accept to send heartbeats, the maximum rate at which the caller can accept to send heartbeats. Note that all these values are in milliseconds!JobType: Service, Task or TopicdSafe read method for JobType!™Connect to a registry: The caller registers itself at the registry. The owner of the registry will then use the caller depending on its purpose.e: Connection to a Stomp broker; -: The name of the job provided by the caller;-: The type of the job provided by the caller; $: The registry's registration queue; ō: The queue to register; this is the queue the register will actually use (for forwarding requests or whatever it does in this specific case). The registry, internally, uses  - together with this queue as a key to identify the provider. Int: Timeout in microseconds;IInt: Preferred heartbeat in milliseconds (0 for no heartbeats). The function returns a tuple of  | and the heartbeat proposed by the registry (which may differ from the preferred heartbeat of the caller). Whenever the   is not  , the heartbeat is 0. If the   is null, the   will be +. If the timeout expires, register throws  ."’vDisconnect from a registry: The caller disconnects from a registry to which it has registered before. For the case that the registry is not receiving heartbeats from the caller, it is essential to unregister, when the service is no longer provided. Otherwise, the registry has no way to know that it should not send requests to this provider anymore.e: Connection to a Stomp broker; : The   to unregister; %: The registry's registration queue ; : The queue to unregister;!Int: The timeout in microseconds.The function returns a  . If   is null, the   will be 4. If the timeout expires, the function will throw  .#Send heartbeats:f : An MVar of type y, this MVar will be used to keep track of when the heartbeat has actually to be sent.gė (): The writer through which to send the heartbeat; The queue name of the writer is the registration queue of the registry; note that its type is (): heartbeats are empty messages. : The   for which to send heartbeats; ): The queue for which to send heartbeats.$Map action to  s of job  (; mapping means different things for:÷Serice, Task: action is applied to the first active provider of a list of providers and this provider is then sent to the back of the list, hence, implementing a balancer.WTopic: action is applied to all providers, hence, implementing a publisher. Parameters:: The registry to use; ': The job to which to apply the action;( -> IO ()): The action to apply.ŲThe function returns False iff the requested job is not available and True otherwise. (Note that a job without providers is removed; when the function returns True, the job, thus, was applied at least once.hMap function of type  'Provider' -> 'Provider'to all  s of job   (independent of )% Retrieves n 2s of a certain job; getProvider works, for all +s according to the work balancer logic, i.e.l: it returns the first n providers of the list for this job and moves them to the end of the list. %- is used, for instance, in the Desk pattern. : The registry in use; 0: The job for which the caller needs a provider;Int: The number n3 of providers to retrieve; if less than nƒ providers are available for this job, all available providers will be returned, but no error event is created.&tThis function shows all jobs with all their providers in a registry; the function is intended for debugging only.'«A registry is used through a function that, internally, creates a registry and defines its lifetime in terms of the scope of an action passed in to the function:e: Connection to a Stomp broker;5String: Name of the registry used for error handling; J: Name of the registration queue. It is this queue to which !, sends a registration request;<(Int, Int): Minimal and maximal accepted heartbeat interval;: Error handler;(Ž -> IO r): The action that defines the registry's lifetime; the result of this action, r4, is also the result of  withRegistry.i[Get JobQueue (and throw an exception if at least one of the headers does not exist)jVGet Message Type from headers (and throw an exception if the header does not exist)(RGet Job name from headers (and throw an exception if the header does not exist))_Get Reply queue (channel) from headers (and throw an exception if the header does not exist)*TGet Queue name from headers (and throw an exception if the header does not exist)+wGet Job type from headers (and throw an exception if the header does not exist or contains an invalid value),ˆGet Heartbeat specification from headers (and throw an exception if the header does not exist or if its value is not numeric)-UGet Status code from headers (and throw an exception if the header does not exist).iGet Generic function to retrieve a header value (and throw an exception if the header does not exist): String: Key of the wanted header5String: Error message in case there is no such headerk/ m: The message whose headers we want to search3lmnopqrstuvwx d!"#yz{|}~€$h%&'‚ƒij()*+,-.„2lmnopqrstuvwx d!"#yz{|}~€$h%&'‚ƒij()*+,-.$lmnopqrstuvwx d!"#yz{|}~€$h%&'‚ƒij()*+,-.„(c) Tobias SchoofsLGPL  experimentalportableNone/’$The Pusher data type, which implements the consumer side of the pipeline protocol. Note that, when we say "consumer" here, the pusher is actually a data producer, but consumes the effect of having a task done. The pusher can be seen as a client that does not expect a reply.0Access to the pusher's name1Subscriber data type2Access to the subscriber name3The publisher data type4#Access to the name of the publisher5XThe server data type, which implements the server side of the client/server protocol.6Access to the server name7XThe client data type, which implements the client side of the client/server protocol.8Access to the client name9:The function creates a client that lives within its scope. Parameters:e: Connection to a Stomp broker…<: Name of the Client, which can be used for error reporting. : Name of the   the client will request† i: Description of a reader queue; this is the queue through which the server will send its response.‡Ž o: Description of a writer queue; this is the queue through which the server is expecting requests.7q i o -> IO r: An application-defined action whose scope defines the client's lifetime:)The client will send the request of type oO and wait for the reply until the timeout exprires. The reply is of type i and is returned as k iX. If the timeout expires before the reply has been received, the function returns ˆ.;Since servers do not know the clients they are serving, :) sends the name of its reader queue (the  reply queue%) as message header to the server. Parameters:7 i o: The client; note that i is the type of the reply, o is the type of the request.‰: The timeout in microseconds.Š: The MIME type of the request.[‹N]: List of additional headers to be sent with the request.o: The request ;’‰This function serves as a "delayed" receiver for the case that the timeout of a request has expired. When using this function, it is assumed that a request has been made, but no response has been received. It can be used in time-critical applications, where the client may use the time between request and reply productively, instead of passively blocking on the reply queue.±Use this function with care! It can be easily abused to break the client/server pattern, when it is called without a request having been made before. If, in this case, timout is -1(, the application will block forever.,The function receives those parameters from :- that are related to receiving the reply, i.e. Š, [‹] and o are not passed to  checkRequest.<qThe function creates a server that lives within the scope of the application-defined action passed into it. Parameters:e: Connection to a Stomp broker…<: Name of the Server, which can be used for error reporting.†“ i: Description of a reader queue; this is the queue through which clients are expected to send requests.‡’ o: Description of a writer queue; this is the queue through which a specific client will expect the reply. Note that the server will overwrite the destination of this queue using Œa; the destination of this queue, hence, is irrelevant.5q i o -> IO r: An application-defined action whose scope defines the server's lifetime=’KWaits for a client request, calls the application-defined transformer to generate a reply and sends this reply through the reply queue whose name is indicated by a header in the request. The time a server waits for a request may be restricted by the timeout. Typically, you would call reply with timeout set to -1 (wait eternallyj). There may be situations, however, where it actually makes sense to restrict the waiting time, i.e.) to perform some housekeeping in between.(Typically, you call reply in a loop like &forever $ reply srv (-1) nullType [] fwhere f is a function of type  Message i -> IO o. Parameters:5o i o: The server; note that i is the request queue and o the reply queue.‰: The timeout in microseconds.Š: The MIME type of the reply.[‹0]: Additional headers to be sent with the reply.kœ i -> IO o: Transforms the request into a reply - this defines the service provided by this application.>ūCreate a server that works in a background thread: The background thread (and with it the server) is running until the action passed in to the function (IO r) terminates; when it terminates, the background thread is terminated as well. withServerThread« may connect to a registry (to serve as a provider of a balancer for instance), which is automatically handled internally when a RegistryDesc is passed in with a   that is not null.  e: Connection to a Stomp broker;…3: The name of the server, used for error reporting; !: The job provided by this serverŠ: The MIME Type (passed to =)[‹!]: Additional headers (passed to =)ke i -> IO o: The core of the reply function: transforming a request of type i0 into a reply of type o†3 i: The reader through which requests are expected;‡. o: The writer through which replies are sent;’N: Describes whether and how to connect to a registry: if the queue name of the registry description is null, the function will not connect to a registry; otherwise it will connect to the registry proposing the best value of the Ī as its preferred heartbeat rate; should the heartbeat rate returned by the registry be outside the scope of min and max, withServerThread+ will terminate with .: Error handlerčIO r: The function starts a new thread on which the the server is working; the thread from which the function was called continues in this action. Its return value is also the result of withServerThread\. When the action terminates, the new thread is terminated internally.?’÷Create a publisher with the lifetime of the scope of the user action passed in. The publisher, internally, creates a registry to which subscribers will connect to obtain the topic data. The registry will not expect heartbeats from subscribers, since the dependability relation is the other way round: the publisher does not depend on subscribers, but subscribers depend on a publisher. The publisher, usually, does not send heartbeats either. For exceptions to this rule, see I.e: Connect to a Stomp broker;7String: Name of the publisher used for error reporting; : The name of the topic; &: Name of the registration queue (see ');': Error Handler passed to the registry;‡Õ: Queue through which data are published; note that the queue name is irrelevant. The publisher will send data to the queues of registered subscribers (see $);3^ -> IO r: Action that defines the lifetime of the publisher; the result (r-) is also the result of withPub.@Publish data of type o:3 o: Publisher to use;Š&: MIME Type of the message to be sent;[‹2]: Additional headers to be sent with the message;o: The message content.AhCreate a publisher that works in a background thread publishing periodically at a monotonic rate, i.e.’ it creates data and publishes them, computes the difference of the publication rate minus the time needed to create and publish the data and will then suspend the thread for this period. For a publication rate of p< microseconds, the thread will be delayed for p - x microseconds, if x] corresponds to the time that was spent on creating and publishing the data.©The precision depends of course on your system and its current workload. For most cases, this will be equal to just suspending the thread for the publication rate. Parameters: e: Connection to a Stomp broker;7String: Name of the publisher used for error reporting; : Name of the topic; : Registration queue;)Type: MIME Type of the published message;[‹D]: Additional headers to be sent with the message;+IO o: Action to create the message content;‡• o: Queue through which the message will be published (remember, however, that the queue name is irrelevant);&Int: Publication rate in microseconds;B: Error handler for the registry and the publisher;NIO r: Action that defines the lifetime of the publisher; The result r is also the result of  withPubThread. B¾Create a subscriber with the lifetime of the user action passed in. The subscriber will internally connect to a publisher's registry and receive data as long as it stays connected.e: Connection to a Stomp broker;3String: Subscriber name useful for error reporting; : Subscribed topic; 7: Queue of a registry to connect to (the s registration queue!)*Int: Registration timeout in microseconds;†V: This is the queue through which the subscriber will receive data.1b i -> IO r: Action that defines the lifetime of the subscriber. Its result r. is also the result of withSub.C…Check if data have been arrived for this subscriber; if data are available before the timeout expires, the function results in Ž (k4 i); if the timeout expires first, the result is ˆ.1 i: The subscriber to check Int: Timeout in microsecondsDøCreate a subscriber that works in a background thread; Whenever data are available, an application callback passed in to the function is called with the message that has arrived. e: Connection to a Stomp broker;1String: Subscriber name used for error reporting; : Subscribed topic; %: The publisher's registration queue;*Int: Registration timeout in microseconds;†O i: Queue through which the subscriber shall receive data;k" i -> IO (): Application callback;: Error handler; PIO r: Action that defines the lifetime of the subscriber; the result r is also the result of  withSubThread.ECreate a subscriber that works in a background thread and updates an MVar, whenever new data are available; the function is in fact a special case of D¬, where the application callback updates an MVar. Note that the MVar must not be empty when the function is called, otherwise, it will block on modifying the MVar. e: Connection to a Stomp broker;1String: Subscriber name used for error reporting; : Subscribed topic; %: The publisher's registration queue;*Int: Registration timeout in microseconds;†O i: Queue through which the subscriber shall receive data;f i: MVar to update;: Error handler; PIO r: Action that defines the lifetime of the subscriber; the result r is also the result of  withSubMVar.F Create a + with the lifetime of the action passed in:e: Connection to a Stomp broker;BString: Name of the pusher, which may be used for error reporting; +: Name of the job requested by this pusher;‡ o: gN queue through which the job request is pushed;(/d o -> IO r): Action that defines the lifetime of the pusher; the result r3 is also the result of F.GPush a Job:/ o: The pusher to be used;Š*: The MIME Type of the message to be sent;[‹+]: The headers to be sent with the message;o: The message contents.H`On the other side of the pipeline, there sits a worker waiting for requests. Note that no Worker3 data type is defined. Instead, there is only a withTaskThread’( function that, internally, creates a worker acting in a background thread. The rationale is that it does not make too much sense to have a pipeline with only one worker. It is in fact part of the idea of the pipeline pattern that several workers are used through a balancer. withTaskThreadą implements the interaction with the registry internally and frees the programmer from concerns related to registration. If you really need a single worker, you can call the function with an empty RegistryDesc, i.e. with an empty queue name.e: Connection to a Stomp broker;4String: Name of the worker used for error reporting; ': Name of the job, the worker provides;(k’ i -> IO ()): The job provided by the worker. Note that the function does not return a value: Since workers do not produce a reply, no result is necessary;†J i: Queue through which the worker receives requests;,: The registry to which the worker connects;OnError: Error handler;0IO r: Action that defines the worker's lifetime.I’Unlike servers and workers, publishers have no interface to connect internally to a registry. The rationale for this is that publishers do not need load balancers or similar means that would require registration. As a consequence, there is no means to send heartbeats internally. Sometimes, however, the need to connect to a registry may arise. The Desk pattern is an example where it makes sense to register a publisher. But then, there is no means to internally send heartbeats proving that the publisher is still alive. For this case, a simple solution for periodic publishers is available: a heartbeat proxy that is implemented as a subscriber receiving data from the publisher and sending a heartbeat on every dataset that arrives.zThis function provides a proxy that internally connects to a registry on behalf of a publisher and sends heartbeats.e: Connection to a Stomp broker;3String: Name of the proxy used for error reporting; ,: Name of the topic, the publisher provides; : Registration queue of the publisher - this is the queue to which the internal subscriber connects;†W i: The queue through which the internal subscriber receives data;: The other registry - it is this registry to which the proxy will send heartbeats;: Error Handler;FIO r: Action that definex the proxy's lifetime; its result r is also the result of  withPubProxy../0‘1’2“34”•–—5˜6™š7›8œžŸ9:;<=> ?@ABCDEFGHIJ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIP7889:;566<=!"#>/00FGH'$%& 344?@A122BCDEI   (+*),-./0‘1’2“34”•–—5˜6™š7›8œžŸ9:;<=> ?@ABCDEFGHI(c) Tobias SchoofsLGPL  experimentalportableNoneJxCreate a forwarder with the lifetime of the application-defined action passed in and start it in a background thread: eb: Connection to the source broker (the one where the original publisher is connected);ec: Connection to the target broker (the one where the target subscribers are connected);6String: Name of the forwarder used for error handling; $: Name of the Topic that is bridged; -: Registration queue of the source publisher; n: Queue through which the internal subscriber will receive topic data from the source publisher; -: Registration queue of the target publisher;NInt: Timeout on registering to the source publisher in microseconds;: Error handler;OIO r: Action that defines the lifetime of the forwarder; its result r is also the result of  withForwarder.6Note the remarkable similarity to the router pattern ( withRouterZ). In fact, a router is but a forwarder where source and target broker are the same.KeCreate a TaskBridge with the lifetime of the action passed in and start it on a background thread: eY: Connection to the source broker (the one to which the pusher is connected);eY: Connection to the target broker (the one to which the worker is connected);3String: Name of the bridge used for error handling; #: Name of the Task that is bridged; Ž: Queue of the worker on the source side; (if the worker is connected to a balancer on the source side, this is an internal queue only visible in the bridge and in the balancer); a: Queue of the worker on the target side (which may be a balancer's request queue);:  (i.e.i balancer) to which the bridge is connected on the source side;: Error handler;LIO r: Action that defines the lifetime of the bridge; its result r is also the result of withTaskBridge.LhCreate a ServiceBridge with the lifetime of the action passed in and start it on a background thread: eY: Connection to the source broker (the one to which the client is connected);eY: Connection to the target broker (the one to which the server is connected);3String: Name of the bridge used for error handling; &: Name of the Service that is bridged; Ž: Queue of the server on the source side; (if the server is connected to a balancer on the source side, this is an internal queue only visible in the bridge and in the balancer); 9: Reader queue of the internal client on the target side; a: Queue of the server on the target side (which may be a balancer's request queue);:  (i.e.i balancer) to which the bridge is connected on the source side;: Error handler;LIO r: Action that defines the lifetime of the bridge; its result r is also the result of withServiceBridge.JKLJKLJKLJKL(c) Tobias SchoofsLGPL  experimentalportableNoneMFCreates a desk with the lifetime of the application-defined action:e: Connection to a Stomp broker;2String: Name of the desk, used for error handling; •: Registration queue -- this queue is used by providers to connect to the registry, it is not used for consumer requests;#(Int, Int): Heartbeat range of the  (see ' for details);: Error handling; €: Request queue -- this queue is used by consumers to request information about available providers;`IO r: Action that defines the lifetime of the desk; the result is also the result of withDesk.NJFunction used by consumer to request provider information from a desk:7Ü () (): The request to the desk is sent through a client of type () (). This client must be created by the application beforehand (e.g.’B: the client could be created once during initialisation and then be used repeatedly to obtain or update information on providers according to the application needs);Int: Timeout in microseconds; I: Name of the job for which the consumer needs providers;’Int: Number of providers needed by the consumer. This can be used for redundancy: if one provider fails, the consumer passes to the next. Be aware, however, that the information, at the point in time, when a provider fails, may already be outdated. Therefore, the redundant providers should be used immediately and, when the main provider fails later, the information should be updated by requesting new providers from the desk.The result is a tuple of ( , [  ]). If the   is not , the list of  Q will be empty; otherwise, it will contain at least one provider and maximum n providers (where n} is the number of providers requested). If fewer providers than requested are available, the list will contain less than nl providers. But note that this, as long as there is at least one provider, does not count as an error, i.e. the   is still .MN”¢MNMNMN”¢(c) Tobias SchoofsLGPL  experimentalportableNoneO‹Create a Service and Task Balancer with the lifetime of the application-defined action passed in and start it in a background thread:e: Connection to a Stomp broker;6String: Name of the balancer, used for error handling; •: Registration queue -- this queue is used by providers to connect to the registry, it is not used for consumer requests;#(Int, Int): Heartbeat range of the  (see ' for details); J: Request queue -- this queue is used for consumer requests;: Error handling;NIO r: Action that defines the lifetime of the balancer; the result r is also the result of  withBalancer.PyCreate a router with the lifetime of the application-defined action passed in and start it in a background thread: e: Connection to a Stomp broker;4String: Name of the router, used for error handling; : Routed topic; -: Registration queue of the source publisher; r: Queue through which the internal subscriber will receive the topic data from the source publisher; ]: Registration queue of the target publisher to which subscribers will connect;SInt: Registration timeout (timeout to register at the source publisher); J: Request queue -- this queue is used for consumer requests;: Error handling;LIO r: Action that defines the lifetime of the router; the result r is also the result of  withRouter.OPOPOPOP£      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYWZ[W\]^_`abcdefghijklmnopqrWstpuvwxypz{|}}~€€‚ ƒ„…†‡ˆ‰Š‹ŒŽW‘’pu“pu”W•–—˜™š›œžŸpu ”W•¢£¤„¦§Ø©Ŗ«¬­®Æ°±²³“µ¶·stomp-patterns-0.1.0!Network.Mom.Stompl.Patterns.Basic"Network.Mom.Stompl.Patterns.Bridge Network.Mom.Stompl.Patterns.Desk$Network.Mom.Stompl.Patterns.BalancerTypesRegistryHBOnErrorPatternsExceptionAppX NoProviderXUnacceptableHbX MissingHbXHeaderXNotOKXBadStatusCodeXTimeoutXQNameJobName StatusCodeTimeoutNotFound Forbidden BadRequestOKreadStatusCodenobody ignorebodybytesOutbytesInmkHBProviderprvQ RegistryDescJobTypeTopicTaskServiceregister unRegister heartbeatmapR getProvider showRegistry withRegistry getJobName getChannelgetQueue getJobTypegetHBgetSC getHeaderPusherApushNameSubAsubNamePubApubNameServerAsrvNameClientAclName withClientrequest checkRequest withServerreplywithServerThreadwithPubpublish withPubThreadwithSub checkIssue withSubThread withSubMVar withPusherpushwithTaskThread withPubProxy withForwarderwithTaskBridgewithServiceBridgewithDeskrequestProvider withBalancer withRouterbase GHC.Exception SomeExceptionGHC.ReadRead Text.Readreadbytestring-0.10.4.0Data.ByteString.Internal ByteStringhbMehbMeNextreThrowHandler ignoreHandler killAndWait withThread tolerancenextHBtimeAdd ms2nominal nominal2us$fExceptionPatternsException$fReadStatusCode$fShowStatusCode readJobTypestomp-queue-0.2.2FactoryConGHC.MVarMVarNetwork.Mom.Stompl.Client.QueueWritermapAllR getJobQueuegetMTypeStateMessageregMRegregNameregWorkJobNodejobTypejobProvsprvHbprvNxt updOrAddProvremProvgetHeads useRegistry useRegistry_insertRupdRremoveRhandleRegisterhandleUnRegisterhandleHeartbeat $fEqProviderGHC.BaseString ReaderDesc WriterDesc Data.MaybeNothingghc-prim GHC.TypesInt mime-0.4.0.2Codec.MIME.TypeType stompl-0.3.0Network.Mom.Stompl.FrameHeader writeAdHocPubJustPusherpushJobpushQSubsubInpubJobpubRegpubConvpubOutSrvsrvInsrvOutClclChnclJobclInclOutfinalisegetJobs getRedundancy