patterns-0.1.0: src/Network/Mom/Patterns/Broker/Client.hs
-------------------------------------------------------------------------------
-- |
-- Module : Network/Mom/Patterns/Broker/Client.hs
-- Copyright : (c) Tobias Schoofs
-- License : LGPL
-- Stability : experimental
-- Portability: non-portable
--
-- Majordomo Client
-------------------------------------------------------------------------------
module Network.Mom.Patterns.Broker.Client (
Client, withClient, checkService,
request)
where
import qualified Data.ByteString.Char8 as B
import Control.Monad.Trans (liftIO)
import Control.Exception (throwIO)
import Data.Conduit (($=), (=$))
import qualified Data.Conduit as C
import qualified System.ZMQ as Z
import Network.Mom.Patterns.Types
import Network.Mom.Patterns.Streams
import Network.Mom.Patterns.Broker.Common
-------------------------------------------------------------------------
-- | Client data type
-------------------------------------------------------------------------
data Client = Client {clSock :: Z.Socket Z.XReq,
clService :: Service}
------------------------------------------------------------------------
-- | Create a client and start the action,
-- in whose scope the client lives;
-- * 'Context' - The zeromq context
--
-- * 'Service' - The client service name;
-- the client will be specialised
-- for requesting this service.
--
-- * 'String' - The address to connect to.
--
-- * 'LinkType' - This parameter is ignored;
-- the majordomo protocol expects clients
-- to connect to the broker.
--
-- * 'Client' -> IO a - The action loop
------------------------------------------------------------------------
withClient :: Context ->
Service ->
String ->
LinkType ->
(Client -> IO a) -> IO a
withClient ctx srv add _ act =
Z.withSocket ctx Z.XReq $ \s -> do
link Connect s add []
act $ Client s srv
------------------------------------------------------------------------
-- | Service discovery:
-- The function checks whether the client's service
-- is provided by the broker.
--
-- Return values:
--
-- * Nothing: The broker timed out
--
-- * Just False: The service is not available
--
-- * Just True: The service is available
------------------------------------------------------------------------
checkService :: Client -> Timeout -> IO (Maybe Bool)
checkService c tmo = do
runSender (clSock c) $ mdpSrc mmi (C.yield $ B.pack $ clService c)
runReceiver (clSock c) tmo $ mdpSnk mmi mmiResponse
where mmi = B.unpack $ mmiHdr `B.append` mmiSrv
mmiResponse = do
mbX <- C.await
case mbX of
Nothing -> liftIO (putStrLn "Timeout!") >> return Nothing
Just x | x == mmiFound -> return $ Just True
| x == mmiNotFound -> return $ Just False
| x == mmiNimpl -> liftIO (throwIO $
MMIExc "MMI Service Request not available")
| otherwise -> liftIO (throwIO $
MMIExc $ "Unexpected response code " ++
"from mmi.service request: " ++
B.unpack x)
------------------------------------------------------------------------
-- | Request a service:
--
-- * 'Client' - The client, through which the service is requested
--
-- * 'Timeout' - Timeout in microseconds, -1 to wait eternally.
-- With timeout = 0, the function returns immediately
-- with 'Nothing'.
-- When the timeout expires, request is abandoned.
-- In this case, the result of the request
-- is Nothing.
--
-- * 'Source' - The source of the request stream;
-- the format of the request will probably comply
-- with some communication protocol,
-- as, for instance, in the majordomo pattern.
--
-- * 'SinkR' - The sink receiving the reply. The result of the sink
-- is returned as the request's overall result.
-- Note that the sink may perform different
-- actions on the segments of the resulting stream,
-- /e.g./ storing data in a database,
-- and return the number of records received.
------------------------------------------------------------------------
request :: Client -> Timeout -> Source -> SinkR (Maybe a) -> IO (Maybe a)
request c tmo src snk = do
runSender (clSock c) $ mdpSrc (clService c) src
runReceiver (clSock c) tmo $ mdpSnk (clService c) snk
{- currently not provided
checkReceive :: Client -> Timeout -> SinkR (Maybe a) -> IO (Maybe a)
checkReceive c tmo snk =
runReceiver (clSock c) tmo $ mdpSnk (clService c) snk
-}
------------------------------------------------------------------------
-- Add MDP headers to a stream created by source
------------------------------------------------------------------------
mdpSrc :: Service -> Source -> Source
mdpSrc sn src = src $= mdpCSndReq sn
------------------------------------------------------------------------
-- Parse and remove the MDP headers
-- before the stream is passed over to a sink
------------------------------------------------------------------------
mdpSnk :: Service -> SinkR (Maybe a) -> SinkR (Maybe a)
mdpSnk sn snk = mdpCRcvRep sn =$ snk