yaml-rpc-0.1: Network/YAML/Caller.hs
{-# LANGUAGE OverloadedStrings #-}
module Network.YAML.Caller where
import qualified Data.Map as M
import Data.Object.Yaml
import Data.Convertible.Base
import qualified Data.ByteString.Char8 as BS
import Network
import System.IO
import Control.Monad
import Control.Concurrent
import Network.YAML.Base
import Network.YAML.Instances
import Network.YAML.Server
-- | Send any YAML text and return an answer
sendYAML :: (BS.ByteString, Int) -- ^ (Hostname, port)
-> BS.ByteString -- ^ YAML text
-> IO BS.ByteString -- ^ Answer
sendYAML (host,port) yaml = withSocketsDo $ do
h <- connectTo (BS.unpack host) (PortNumber $ fromIntegral port)
hSetBuffering h NoBuffering
BS.hPutStrLn h yaml
lns <- readHandle h []
hClose h
let text = BS.unlines lns
return text
-- | Call remote method
call :: (IsYamlObject a, IsYamlObject b)
=> (BS.ByteString, Int) -- ^ (Host name, port number)
-> BS.ByteString -- ^ Name of method
-> a -- ^ Argument for method
-> IO b
call (host,port) name args = do
let c = mkCall name (cs args)
s = serialize c
text <- sendYAML (host,port) s
case unserialize text of
Nothing -> fail "No answer"
Just x -> return x
-- | Similar, but select server on each call
callDynamic :: (IsYamlObject a, IsYamlObject b)
=> (BS.ByteString -> IO (BS.ByteString,Int)) -- ^ Get (Host name, port number) from service name
-> BS.ByteString -- ^ Name of the service
-> BS.ByteString -- ^ Name of method
-> a -- ^ Argument for method
-> IO b
callDynamic getServer service name args = do
srv <- getServer service
call srv name args
-- | Call a method and put it's result into MVar
callF :: (IsYamlObject a, IsYamlObject b)
=> (BS.ByteString -> IO (BS.ByteString, Int)) -- ^ Get (Host, port) from service name
-> BS.ByteString -- ^ Service name
-> BS.ByteString -- ^ Method name
-> (a, MVar b) -- ^ (Argument, MVar for result)
-> IO ()
callF getServer service name (args, var) = do
srv <- getServer service
-- putStrLn $ "Calling to " ++ show srv
r <- call srv name args
putMVar var r
-- | Call a method for each argument in the list in parallel
-- (it can run method for each argument on another server)
callP :: (IsYamlObject a, IsYamlObject b)
=> (BS.ByteString -> IO (BS.ByteString, Int)) -- ^ Get (Host, port) from service name
-> BS.ByteString -- ^ Service name
-> BS.ByteString -- ^ Method name
-> [a] -- ^ List of arguments
-> IO [b]
callP getServer service name args = do
let n = length args
vars <- replicateM n newEmptyMVar
mapM (forkIO . callF getServer service name) $ zip args vars
mapM takeMVar vars