2 -- generic AMQP publisher
3 import Control.Concurrent
4 import qualified Control.Exception as X
5 import Control.Monad (forever)
6 import qualified Data.ByteString.Lazy.Char8 as BL
7 #if MIN_VERSION_hinotify(0,3,10)
8 import qualified Data.ByteString.Char8 as BS
10 import Data.List (isSuffixOf)
12 import qualified Data.Text as T
14 import Data.Time.Clock.POSIX
15 import Data.Version (showVersion)
16 import Data.Word (Word64)
19 import Network.AMQP.Types
20 import Network.AMQP.Utils.Connection
21 import Network.AMQP.Utils.Helpers
22 import Network.AMQP.Utils.Options
23 import Paths_amqp_utils (version)
24 import System.Environment
27 import qualified System.Posix.Files as F
33 args <- getArgs >>= parseargs 'a'
34 printparam "client version" ["amqp-utils", showVersion version]
35 printparam "routing key" $ rKey args
36 printparam "exchange" $ currentExchange args
38 if inputFile args == "-"
40 else F.getFileStatus (inputFile args) >>= return . F.isDirectory
42 then printparam "hotfolder" $ inputFile args
50 (conn, chan) <- connect args
51 addChannelExceptionHandler chan (X.throwTo tid)
52 printparam "confirm mode" $ confirm args
55 confirmSelect chan False
56 addConfirmationListener chan confirmCallback
58 let publishOneMsg = publishOneMsg' chan args
62 inotify <- initINotify
67 #if MIN_VERSION_hinotify(0,3,10)
68 (BS.pack (inputFile args))
72 (handleEvent publishOneMsg (suffix args) (inputFile args))
73 hr $ "BEGIN watching " ++ (inputFile args)
74 _ <- forever $ threadDelay 1000000
76 hr $ "END watching " ++ (inputFile args)
80 if inputFile args == "-"
82 else BL.readFile (inputFile args)
84 then mapM_ (publishOneMsg Nothing) (BL.lines messageFile)
85 else publishOneMsg (Just (inputFile args)) messageFile
88 -- all done. wait and close.
90 then waitForConfirms chan >>= printparam "confirmed"
92 X.catch (closeConnection conn) exceptionHandler
94 -- | A handler for clean exit
95 exceptionHandler :: AMQPException -> IO ()
96 exceptionHandler (ChannelClosedException Normal txt) = printparam "exit" txt >> exitWith ExitSuccess
97 exceptionHandler (ConnectionClosedException Normal txt) = printparam "exit" txt >> exitWith ExitSuccess
98 exceptionHandler x = printparam "exception" x >> exitWith (ExitFailure 1)
100 -- | The handler for publisher confirms
101 confirmCallback :: (Word64, Bool, AckType) -> IO ()
102 confirmCallback (deliveryTag, isAll, ackType) =
112 -- | Hotfolder event handler
114 (Maybe String -> BL.ByteString -> IO ())
119 -- just handle closewrite and movedin events
120 #if MIN_VERSION_hinotify(0,3,10)
121 handleEvent f s p (Closed False (Just x) True) = handleFile f s (p ++ "/" ++ (BS.unpack x))
122 handleEvent f s p (MovedIn False x _) = handleFile f s (p ++ "/" ++ (BS.unpack x))
124 handleEvent f s p (Closed False (Just x) True) = handleFile f s (p ++ "/" ++ x)
125 handleEvent f s p (MovedIn False x _) = handleFile f s (p ++ "/" ++ x)
127 handleEvent _ _ _ _ = return ()
129 -- | Hotfolder file handler
131 (Maybe String -> BL.ByteString -> IO ()) -> [String] -> FilePath -> IO ()
132 handleFile _ _ ('.':_) = return () -- ignore hidden files
133 handleFile f s@(_:_) x =
134 if any (flip isSuffixOf x) s
135 then handleFile f [] x
139 (BL.readFile x >>= f (Just x))
140 (\e -> printparam "exception in handleFile" (e :: X.SomeException))
142 -- | Publish one message with our settings
143 publishOneMsg' :: Channel -> Args -> Maybe String -> BL.ByteString -> IO ()
144 publishOneMsg' c a fn f = do
145 printparam "sending" fn
146 (mtype, mencoding) <-
147 if (magic a) && isJust fn
149 m <- magicOpen [MagicMimeType]
151 t <- magicFile m (fromJust fn)
152 magicSetFlags m [MagicMimeEncoding]
153 e <- magicFile m (fromJust fn)
154 return (Just (T.pack t), Just (T.pack e))
155 else return ((contenttype a), (contentencoding a))
156 now <- getCurrentTime >>= return . floor . utcTimeToPOSIXSeconds
159 (T.pack $ currentExchange a)
163 , msgDeliveryMode = persistent a
164 , msgTimestamp = Just now
166 , msgType = msgtype a
167 , msgUserID = userid a
168 , msgApplicationID = appid a
169 , msgClusterID = clusterid a
170 , msgContentType = mtype
171 , msgContentEncoding = mencoding
172 , msgReplyTo = replyto a
173 , msgPriority = prio a
174 , msgCorrelationID = corrid a
175 , msgExpiration = msgexp a
176 , msgHeaders = substheader (fnheader a) fn $ msgheader a
181 [String] -> Maybe String -> Maybe FieldTable -> Maybe FieldTable
182 substheader (s:r) (Just fname) old =
183 substheader r (Just fname) (addheader old (s ++ "=" ++ fname))
184 substheader _ _ old = old