1 -- generic AMQP publisher
2 import Control.Concurrent (threadDelay)
3 import qualified Control.Exception as X
4 import Control.Monad (forever)
5 import qualified Data.ByteString.Lazy.Char8 as BL
6 import Data.List (isSuffixOf)
8 import qualified Data.Text as T
10 import Data.Time.Clock.POSIX
11 import Data.Version (showVersion)
12 import Data.Word (Word64)
15 import Network.AMQP.Types
16 import Network.AMQP.Utils.Connection
17 import Network.AMQP.Utils.Helpers
18 import Network.AMQP.Utils.Options
19 import Paths_amqp_utils (version)
20 import System.Environment
22 import qualified System.Posix.Files as F
27 args <- getArgs >>= parseargs "agitprop"
28 printparam' "client version" $ "amqp-utils " ++ (showVersion version)
29 printparam' "routing key" $ rKey args
30 isDir <- F.getFileStatus (inputFile args) >>= return . F.isDirectory
32 then printparam' "hotfolder" $ inputFile args
33 else printparam' "input file" $
36 then " (line-by-line)"
38 (conn, chan) <- connect args
39 printparam' "confirm mode" $ show $ confirm args
42 confirmSelect chan False
43 addConfirmationListener chan confirmCallback
45 let publishOneMsg = publishOneMsg' chan args
49 inotify <- initINotify
55 (handleEvent publishOneMsg (suffix args) (inputFile args))
56 hr $ "BEGIN watching " ++ (inputFile args)
57 _ <- forever $ threadDelay 1000000
59 hr $ "END watching " ++ (inputFile args)
62 messageFile <- BL.readFile (inputFile args)
64 then mapM_ (publishOneMsg Nothing) (BL.lines messageFile)
65 else publishOneMsg (Just (inputFile args)) messageFile
67 (\exception -> printparam' "exception" $ show (exception :: X.SomeException))
68 -- all done. wait and close.
70 then waitForConfirms chan >>= (printparam' "confirmed") . show
73 hr "connection closed"
75 -- | The handler for publisher confirms
76 confirmCallback :: (Word64, Bool, AckType) -> IO ()
77 confirmCallback (deliveryTag, isAll, ackType) =
80 ((show deliveryTag) ++
86 -- | Hotfolder event handler
88 (Maybe String -> BL.ByteString -> IO ())
93 -- just handle closewrite and movedin events
94 handleEvent f s p (Closed False (Just x) True) = handleFile f s (p ++ "/" ++ x)
95 handleEvent f s p (MovedIn False x _) = handleFile f s (p ++ "/" ++ x)
96 handleEvent _ _ _ _ = return ()
98 -- | Hotfolder file handler
100 (Maybe String -> BL.ByteString -> IO ()) -> [String] -> FilePath -> IO ()
101 handleFile _ _ ('.':_) = return () -- ignore hidden files
102 handleFile f s@(_:_) x =
103 if any (flip isSuffixOf x) s
104 then handleFile f [] x
108 (BL.readFile x >>= f (Just x))
110 printparam' "exception in handleFile" $
111 show (exception :: X.SomeException))
113 -- | Publish one message with our settings
114 publishOneMsg' :: Channel -> Args -> Maybe String -> BL.ByteString -> IO ()
115 publishOneMsg' c a fn f = do
116 printparam "sending" fn
117 (mtype, mencoding) <-
118 if (magic a) && isJust fn
120 m <- magicOpen [MagicMimeType]
122 t <- magicFile m (fromJust fn)
123 magicSetFlags m [MagicMimeEncoding]
124 e <- magicFile m (fromJust fn)
125 return (Just (T.pack t), Just (T.pack e))
126 else return ((contenttype a), (contentencoding a))
127 now <- getCurrentTime >>= return . floor . utcTimeToPOSIXSeconds
131 (T.pack $ currentExchange a)
135 , msgDeliveryMode = persistent a
136 , msgTimestamp = Just now
138 , msgType = msgtype a
139 , msgUserID = userid a
140 , msgApplicationID = appid a
141 , msgClusterID = clusterid a
142 , msgContentType = mtype
143 , msgContentEncoding = mencoding
144 , msgReplyTo = replyto a
145 , msgPriority = prio a
146 , msgCorrelationID = corrid a
147 , msgExpiration = msgexp a
148 , msgHeaders = substheader (fnheader a) fn $ msgheader a
150 printparam "sent" $ fmap show r
153 [String] -> Maybe String -> Maybe FieldTable -> Maybe FieldTable
154 substheader (s:r) (Just fname) old =
155 substheader r (Just fname) (addheader old (s ++ "=" ++ fname))
156 substheader _ _ old = old