packages feed

yaml-rpc-0.3: Network/YAML/Caller.hs

{-# LANGUAGE OverloadedStrings, FlexibleInstances, TypeSynonymInstances #-}

module Network.YAML.Caller
  (callDynamic,
   callF,
   callP
  )
  where

import qualified Data.Map as M
import Data.Object.Yaml
import qualified Data.ByteString.Char8 as BS
import Network
import System.IO
import Control.Monad
import Control.Concurrent

import Network.YAML.Types
import Network.YAML.Base (serialize, unserialize)
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

-- | Send any YAML text and return an answer
hSendYAML :: Handle
         -> BS.ByteString             -- ^ YAML text
         -> IO BS.ByteString          -- ^ Answer
hSendYAML h yaml =  withSocketsDo $ do
  hSetBuffering h NoBuffering
  BS.hPutStrLn h yaml
  lns <- readHandle h []
  let text = BS.unlines lns
  return text

instance Connection HostAndPort where
  -- | 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 (toYaml args)
        s = serialize c
    text <- sendYAML (host,port) s
    case unserialize text of
      Nothing -> fail "No answer"
      Just x -> return x

  newConnection pair = return pair
  closeConnection _ = return ()

instance Connection PersistentConnection where
  newConnection (host, port) = do
    h <- connectTo (BS.unpack host) (PortNumber $ fromIntegral port)
    return (PC h)

  closeConnection (PC h) = hClose h

  call (PC h) name args = do
    let c = mkCall name (toYaml args)
        s = serialize c
    text <- hSendYAML h s
    case unserialize text of
      Nothing -> fail "No answer"
      Just x -> return x

-- | Similar to call, 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