{-# LANGUAGE CPP, ScopedTypeVariables #-}
module Transient.Move.Utils (initNode,initNodeDef, initNodeServ, inputNodes, simpleWebApp, initWebApp
, onServer, onBrowser, atServer, atBrowser, runTestNodes, showURL)
where
import Transient.Internals
import Transient.Logged
import Transient.Move.Internals
import Control.Applicative
import Control.Monad.State
import Data.IORef
import System.Environment
import System.IO.Error
import Data.Typeable
import Data.List((\\), isPrefixOf)
import qualified Data.ByteString.Char8 as BS
import Control.Exception hiding(onException)
import System.IO.Unsafe
rretry= unsafePerformIO $ newIORef False
initNode :: Loggable a => Cloud a -> TransIO a
initNode app= do
node <- getNodeParams
rport <- liftIO $ newIORef $ nodePort node
node' <- return node `onException'` ( \(e :: IOException) -> do
if (ioeGetErrorString e == "resource busy")
then do
liftIO $ putStr "Port busy: " >> print (nodePort node)
retry <- liftIO $ readIORef rretry
if retry then do liftIO $ print "retrying with next port" ;continue else empty
port <- liftIO $ atomicModifyIORef rport $ \p -> (p+1,p+1)
return node{nodePort= port}
else return node )
return () !> ("NODE", node')
initWebApp node' app
getNodeParams :: TransIO Node
getNodeParams =
if isBrowserInstance then liftIO createWebNode else
#ifdef ghcjs_HOST_OS
empty
#else
do
oneThread $ option "start" "re/start node"
host <- input' (Just "localhost") (const True) "hostname of this node. (Must be reachable, default:localhost)? "
retry <-input' (Just "n") (== "retry") "if you want to retry with port+1 when fail, write 'retry': "
when (retry == "retry") $ liftIO $ writeIORef rretry True
port <- input (const True) "port to listen? "
liftIO $ createNode host port
<|> getCookie
where
getCookie= do
if isBrowserInstance then return() else do
option "cookie" "set the cookie"
c <- input (const True) "cookie: "
liftIO $ writeIORef rcookie c
empty
#endif
initNodeDef :: Loggable a => String -> Int -> Cloud a -> TransIO a
initNodeDef host port app= do
node <- def <|> getNodeParams
initWebApp node app
where
def= do
args <- liftIO getArgs
if null args then liftIO $ createNode host port else empty
initNodeServ :: Loggable a => Service -> String -> Int -> Cloud a -> TransIO a
initNodeServ services host port app= do
node <- def <|> getNodeParams
let node'= node{nodeServices=[services]}
initWebApp node' $ app
where
def= do
args <- liftIO getArgs
if null args then liftIO $ createNode host port else empty
inputNodes :: Cloud empty
inputNodes= onServer $ do
local $ abduce >> labelState (BS.pack "inputNodes")
listNodes <|> addNew
where
addNew= do
local $ do
option "add" "add a new node"
return ()
host <- local $ do
r <- input (const True) "Hostname of the node (none): "
if r == "" then stop else return r
port <- local $ input (const True) "port? "
serv <- local $ nodeServices <$> getMyNode
services <- local $ input' (Just serv) (const True) ("services? ("++ show serv ++ ") ")
connectit <- local $ input (\x -> x=="y" || x== "n") "connect to the node to interchange node lists? (n) "
nnode <- localIO $ createNodeServ host port services
if connectit== "y" then connect' nnode
else local $ do
liftIO $ putStr "Added node: ">> print nnode
addNodes [nnode]
empty
listNodes= do
local $ option "nodes" "list nodes"
local $ do
nodes <- getNodes
liftIO $ putStrLn "list of nodes known in this node:"
liftIO $ mapM (\(i,n) -> do putStr (show i); putChar('\t'); print n) $ zip [0..] nodes
empty
showURL= onAll$ do
Closure closRemote <- getSData <|> return (Closure 0 )
log <- getLog
n <- getMyNode
liftIO $ do
putStr "'http://"
putStr $ nodeHost n
putStr ":"
putStr $show $ nodePort n
putStr "/"
putStr $ show 0
putStr "/"
putStr $ show closRemote
putStr "/"
putStr $ show $ fulLog log
putStrLn "'"
simpleWebApp :: (Typeable a, Loggable a) => Integer -> Cloud a -> IO ()
simpleWebApp port app = do
node <- createNode "localhost" $ fromIntegral port
keep $ initWebApp node app
return ()
initWebApp :: Loggable a => Node -> Cloud a -> TransIO a
initWebApp node app= do
conn <- defConnection
liftIO $ writeIORef (myNode conn) node
setNodes [node]
serverNode <- getWebServerNode :: TransIO Node
mynode <- if isBrowserInstance
then do
addNodes [serverNode]
return node
else return serverNode
runCloud' $ do
listen mynode <|> return()
serverNode <- onAll getWebServerNode
wormhole serverNode app
runTestNodes ports= do
nodes <- onAll $ mapM (\p -> liftIO $ createNode "localhost" p) ports
onAll $ addNodes nodes
foldl (<|>) empty (map listen1 nodes) <|> return()
where
listen1 n= do
listen n
onAll $ do
ns <- getNodes
addNodes $ n: (ns \\[n])
conn <- getState <|> error "runTestNodes error"
liftIO $ writeIORef (myNode conn) n