packages feed

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

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

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

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

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

{-# OPTIONS_GHC -XDeriveDataTypeable #-}
module Main(main) where

import           Control.Concurrent
import           Control.Exception
import           Data.Typeable
import           System.IO

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

import           InterfacesDFunction


data ConnectionException = ConnectionException
  deriving(Typeable, Show)
instance Exception ConnectionException


data ClientData = ClientData {
    cd_DNodeId :: DNodeId
  , cd_id      :: Maybe Int
  , cd_tid     :: ThreadId
  , cd_name    :: String
  , cd_stub    :: ClientInterfaceStub
  , cd_server  :: RemoteServerInterface
  , cd_exit    :: IO ()
  }

type Client = MVar ClientData


-- what to do when we see the server for the first time after a disconnection
positiveHandlerFunction :: Client -> DHandlerId -> IO ()
positiveHandlerFunction cc _
  = do
    putStrLn "positive Trigger"
    client <- takeMVar cc
    clearStdIn
    hFlush stdout
    -- kill the wait thread
    throwTo (cd_tid client) ConnectionException
    -- start the chat thread
    tid <- forkIO $ chatLoop cc
    putMVar cc $ client { cd_tid = tid }


-- what to do when the server becomes unreachable
negativeHandlerFunction :: Client -> DHandlerId -> IO ()
negativeHandlerFunction cc _
  = do
    putStrLn "negative Trigger"
    client <- takeMVar cc
    -- kill the chat thread
    throwTo (cd_tid client) ConnectionException
    -- start the wait thread
    tid <- forkIO $ waitLoop
    putMVar cc $ client { cd_id = Nothing, cd_tid = tid }
    

mkChatClient :: DNodeId -> ThreadId -> ClientInterfaceStub -> RemoteServerInterface -> IO () -> IO Client
mkChatClient myDNodeId tid cifs rsif endfun
  = do
    newMVar $ ClientData myDNodeId Nothing tid "" cifs rsif endfun


clearStdIn :: IO()
clearStdIn
  = do
    b <- hReady stdin
    if b
      then do
        _ <- getChar
        clearStdIn
      else do 
        return ()    


generateExitFunktions :: IO (IO (), IO ())
generateExitFunktions
  = do
    quitMarker <- newEmptyMVar
    return (waitForTermination quitMarker, terminateProgram quitMarker)
    where
    waitForTermination :: MVar () -> IO ()
    waitForTermination qm
      = do
        _ <- takeMVar qm
        return ()
    terminateProgram :: MVar () -> IO ()
    terminateProgram qm
      = do
        putMVar qm ()


waitLoop :: IO ()
waitLoop
  = handle (\ConnectionException -> return ()) $
      do
      putStrLn "waiting for server..."
      hFlush stdout
      threadDelay 10000000
      waitLoop


chatLoop :: Client -> IO ()
chatLoop cc
  = handle (\ConnectionException -> return ()) $
      do
      client <- readMVar cc
      case (cd_id client) of
        (Just cid) -> do
          -- putStr $ (cd_name client) ++ "> "
          hFlush stdout
          msg <- getLine
          case msg of
            "exit" -> do
              (sif_unregister $ cd_server client) cid
              (cd_exit client)
            _ -> (sif_send $ cd_server client) cid msg
        Nothing -> do
          putStr "login with name: "
          hFlush stdout
          cn <- getLine
          mbId <- (sif_register $ cd_server client) cn (cd_DNodeId client) (cd_stub client)
          client' <- takeMVar cc
          putMVar cc $ client' {cd_id = mbId, cd_name = cn}
      chatLoop cc


receiveChatMessage :: ReceiveChatMessageFunction
receiveChatMessage cn msg
  = do
    putStrLn $ "\n" ++ cn ++ "> " ++ msg



main :: IO ()
main
  = do
    initializeLogging []
    myDNodeId <- initDNode $ defaultDNodeConfig ""
    addForeignDNode $ mkDNodeAddress "ChatServer" "april" (fromInteger 7999)
    (waitForTermination, terminateProgram) <- generateExitFunktions
    -- start with the wait loop
    tid <- forkIO waitLoop
    -- create chat client data strukture
    cifs <- createLocalClientInterfaceStub receiveChatMessage
    rsif <- createRemoveServerInterface "ChatServer"
    cc <- mkChatClient myDNodeId tid cifs rsif terminateProgram
    -- register the handlers which will controll the existence of the server
    let dni = mkDNodeId "ChatServer"
    addForeignDNodeHandler True dni (positiveHandlerFunction cc)
    addForeignDNodeHandler False dni (negativeHandlerFunction cc)
    -- wait for the chatLoop to stop by the user
    waitForTermination
    -- clean up
    closeLocalClientInterfaceStub cifs
    deinitDNode