packages feed

Holumbus-Distribution-0.1.0: Examples/Distribution/ChatDFunction/ServerDFunction.hs

-- ----------------------------------------------------------------------------

{- |
  Module     : Holumbus
  Copyright  : Copyright (C) 2009 Stefan Schmidt
  License    : MIT

  Maintainer : Stefan Schmidt (stefanschmidt@web.de)
  Stability  : experimental
  Portability: portable
  Version    : 0.1
  
-}

-- ----------------------------------------------------------------------------

module Main(main) where

import           Control.Concurrent
import qualified Data.Map as Map
import           Data.Maybe
import qualified Data.Set as Set

import           Holumbus.Distribution.DNode
import           Holumbus.Distribution.DFunction
import           Holumbus.Common.Logging

import           InterfacesDFunction



data ServerData = ServerData {
    sd_clients   :: Map.Map Int (String, RemoteClientInterface)
  , sd_names     :: Set.Set String
  , sd_number    :: Int
  }

type Server = MVar ServerData


-- what to do when a client becomes unreachable
negativeHandlerFunction :: Server -> Int -> DHandlerId -> IO ()
negativeHandlerFunction cs cId hdlId
  = do
    putStrLn "negative Trigger"
    unregisterClient cs cId
    -- delete this trigger, it won't be called again
    delForeignHandler hdlId


registerClient :: Server -> RegisterClientFunction
registerClient _ "" _ _ = return Nothing
registerClient cs cn dni stub
  = do
    putStrLn $ "registering: " ++ cn
    server <- takeMVar cs
    if (Set.member cn (sd_names server))
      then do
        putMVar cs server
        return Nothing
      else do
        let sd_clients' = Map.insert (sd_number server) (cn, createRemoteClientInterface stub) (sd_clients server)
            sd_names'   = Set.insert cn (sd_names server)
            sd_number'  = 1 + (sd_number server)
        putMVar cs $ server { sd_clients = sd_clients', sd_names = sd_names', sd_number = sd_number' }
        -- install a handler to remove the client when he becomes unreachable
        addForeignDNodeHandler False dni (negativeHandlerFunction cs (sd_number server))
        return (Just $ sd_number server)


unregisterClient :: Server -> UnregisterClientFunction
unregisterClient cs cId
  = do
    putStrLn $ "unregistering: " ++ (show cId)
    server <- takeMVar cs
    case (Map.lookup cId (sd_clients server)) of
      Nothing -> do
        putMVar cs server
        return ()
      (Just (cn,_)) -> do
        let sd_clients' = Map.delete cId (sd_clients server)
            sd_names'   = Set.delete cn (sd_names server)
        putMVar cs $ server { sd_clients = sd_clients', sd_names = sd_names' }


sendChatMessage :: Server -> SendChatMessageFunction
sendChatMessage cs cId msg
  = do
    putStrLn $ "sending: " ++ (show cId) ++ "> " ++ msg
    server <- readMVar cs
    case (Map.lookup cId (sd_clients server)) of
      Nothing -> do
        -- TODO return true / false to tell the client that his request was accepted or not
        putStrLn $ "warning: unnkown client with id " ++ (show cId) ++ " wants to send message: " ++ msg
        return ()
      (Just (cn,_)) -> do
        -- TODO exception handling here
        let funs = map (\t -> (cif_receive $ snd t) cn msg) $ Map.elems $ Map.filterWithKey (\cId' _ -> cId' /= cId) (sd_clients server)
        sequence_ funs


mkChatServer :: IO Server
mkChatServer = newMVar $ ServerData Map.empty Set.empty 1


main :: IO ()
main
  = do
    initializeLogging []
    initDNode $ (defaultDNodeConfig "ChatServer")
      { dnc_MinPort = (fromInteger 7999), dnc_MaxPort = (fromInteger 7999) }
    cs <- mkChatServer
    sifs <- createLocalServerInterfaceStub
      (registerClient cs)
      (unregisterClient cs)
      (sendChatMessage cs)
    inputLoop
    closeLocalServerInterfaceStub sifs
    deinitDNode


inputLoop :: IO ()
inputLoop
  = do
    msg <- getLine
    case msg of
      "exit" -> return ()
      _ -> inputLoop