-- ---------------------------------------------------------------------------- {- | Module : Holumbus.Network.Site Copyright : Copyright (C) 2008 Stefan Schmidt License : MIT Maintainer : Stefan Schmidt (stefanschmidt@web.de) Stability : experimental Portability: portable Version : 0.1 Just a little Id to help us to decide if two thread are running in the same program or just on the same machine or on differen machines. -} -- ---------------------------------------------------------------------------- module Holumbus.Network.Site ( -- * Datatypes SiteId , SiteMap -- * Operations on the SiteId , getSiteId , getSiteHost , getSiteProcess , isSameHost , isSameProcess , nearestId -- * Operations on the SiteMap , emptySiteMap , addIdToMap , deleteIdFromMap , deleteHostFromMap , isSiteIdMember , getNeighbourSiteIds ) where import Data.Binary import qualified Data.List as List import Network.Socket import System.Posix import Text.XML.HXT.Arrow import qualified Data.Map as Map import qualified Data.Set as Set -- ---------------------------------------------------------------------------- -- Datatypes -- ---------------------------------------------------------------------------- -- | The datatype of the SiteId, it contains the hostname and a processid, -- so it is possible to decide if two site ids belong to the same process -- or the the same computer or are on distinct computers. data SiteId = SiteId HostName ProcessID deriving (Show, Eq, Ord) instance Binary SiteId where put (SiteId hn pid) = put hn >> put (toInteger pid) get = do hn <- get pid <- get return (SiteId hn (fromInteger pid)) instance XmlPickler SiteId where xpickle = xpSiteId xpSiteId :: PU SiteId xpSiteId = xpElem "siteId" $ xpWrap (\(hn, pid) -> SiteId hn (fromInteger pid), \(SiteId hn pid) -> (hn, toInteger pid)) xpSite where xpSite = xpPair (xpElem "hostname" xpText) (xpElem "pid" xpickle) -- | Just a little Map to hold the SiteIds an to get the neighbout Ids. type SiteMap = Map.Map HostName (Set.Set SiteId) -- ---------------------------------------------------------------------------- -- Operations on the SiteId -- ---------------------------------------------------------------------------- -- | Little helper function. getHostName :: IO (HostName) getHostName = do (hn, _) <- getNameInfo [] True False (SockAddrUnix "localhost") return (maybe "localhost" id hn) -- | Gets the SiteId for the calling program. getSiteId :: IO (SiteId) getSiteId = do hn <- getHostName pid <- getProcessID return (SiteId hn pid) -- | Extracts the Hostname from the SiteId. getSiteHost :: SiteId -> HostName getSiteHost (SiteId hn _) = hn -- | Extracts the ProcessID from the SiteId. getSiteProcess :: SiteId -> ProcessID getSiteProcess (SiteId _ pid) = pid -- | Test, if the two Ids are located on the same host. isSameHost :: SiteId -> SiteId -> Bool isSameHost (SiteId hn1 _) (SiteId hn2 _) = hn1 == hn2 -- | Test, if the two Ids are located on the same host an in the same process. isSameProcess :: SiteId -> SiteId -> Bool isSameProcess = (==) -- | The filtering function. -- Splits the given Id-list into three sublists: -- the Ids from the same process, -- the Ids from the same computer, -- all other Ids. -- This is some sort of distance function, a simple one, but for now it -- should do its work. filterSiteIds :: SiteId -> [SiteId] -> ([SiteId],[SiteId],[SiteId]) filterSiteIds _ [] = ([],[],[]) filterSiteIds i ls = (same, local, other) where (same, temp) = List.partition (\s -> isSameProcess i s) ls (local, other) = List.partition (\s -> isSameHost i s) temp -- | Gets the nearest item from an Id-list compared to a given Id. nearestId :: SiteId -> [SiteId] -> Maybe SiteId nearestId s l = nearestId' $ filterSiteIds s l where nearestId' ([], [], []) = Nothing nearestId' ([], [], x:_) = Just x nearestId' ([], x:_, _) = Just x nearestId' (x:_, _, _) = Just x -- ---------------------------------------------------------------------------- -- Operations on the SiteMap -- ---------------------------------------------------------------------------- -- | Empty SiteId-Map. emptySiteMap :: SiteMap emptySiteMap = Map.empty -- | Adds an id to the map. addIdToMap :: SiteId -> SiteMap -> SiteMap addIdToMap i m = Map.alter f hn m where hn = getSiteHost i f Nothing = (Just $ Set.singleton i) f (Just s) = (Just $ Set.insert i s) -- | Deletes an id from the map. deleteIdFromMap :: SiteId -> SiteMap -> SiteMap deleteIdFromMap i m = Map.alter f hn m where hn = getSiteHost i f Nothing = Nothing f (Just s) = filterEmpty $ Set.delete i s filterEmpty s | s == Set.empty = Nothing | otherwise = Just s -- | Delete a hostname an all its ids from the map. deleteHostFromMap :: HostName -> SiteMap -> SiteMap deleteHostFromMap hn m = Map.alter f hn m where f _ = Nothing -- | Test, if the site id is already in the list. isSiteIdMember :: SiteId -> SiteMap -> Bool isSiteIdMember i m = maybe False (\s -> Set.member i s) (Map.lookup hn m) where hn = getSiteHost i -- | Gets all ids which are on the same host, but not -- the original siteid itself. getNeighbourSiteIds :: SiteId -> SiteMap -> Set.Set SiteId getNeighbourSiteIds i m = maybe (Set.empty) (\s -> Set.delete i s) (Map.lookup hn m) where hn = getSiteHost i