1111
1212-include_lib (" rabbit_common/include/rabbit.hrl" ).
1313-include_lib (" rabbit_common/include/rabbit_framing.hrl" ).
14+ -include_lib (" amqp10_common/include/amqp10_framing.hrl" ).
15+ -include_lib (" rabbit/include/mc.hrl" ).
1416-include (" rabbit_event_exchange.hrl" ).
1517
1618-export ([register /0 , unregister /0 ]).
2022
2123-export ([fmt_proplist /1 ]). % % testing
2224
23- -record (state , {vhost ,
24- has_any_bindings
25+ -define (APP_NAME , rabbitmq_event_exchange ).
26+
27+ -record (state , {protocol :: amqp_0_9_1 | amqp_1_0 ,
28+ vhost :: rabbit_types :vhost (),
29+ has_any_bindings :: boolean ()
2530 }).
2631
2732-rabbit_boot_step ({rabbit_event_exchange ,
@@ -65,41 +70,35 @@ exchange(VHost) ->
6570% %----------------------------------------------------------------------------
6671
6772init ([]) ->
73+ {ok , Protocol } = application :get_env (? APP_NAME , protocol ),
6874 VHost = get_vhost (),
6975 X = rabbit_misc :r (VHost , exchange , ? EXCH_NAME ),
7076 HasBindings = case rabbit_binding :list_for_source (X ) of
71- [] -> false ;
72- _ -> true
73- end ,
74- {ok , # state {vhost = VHost ,
77+ [] -> false ;
78+ _ -> true
79+ end ,
80+ {ok , # state {protocol = Protocol ,
81+ vhost = VHost ,
7582 has_any_bindings = HasBindings }}.
7683
7784handle_call (_Request , State ) -> {ok , not_understood , State }.
7885
79- handle_event (_ , # state {has_any_bindings = false } = State ) ->
80- {ok , State };
81- handle_event (# event {type = Type ,
82- props = Props ,
83- timestamp = TS ,
84- reference = none }, # state {vhost = VHost } = State ) ->
85- _ = case key (Type ) of
86- ignore -> ok ;
87- Key ->
88- Props2 = [{<<" timestamp_in_ms" >>, TS } | Props ],
89- PBasic = # 'P_basic' {delivery_mode = 2 ,
90- headers = fmt_proplist (Props2 ),
91- % % 0-9-1 says the timestamp is a
92- % % "64 bit POSIX
93- % % timestamp". That's second
94- % % resolution, not millisecond.
95- timestamp = erlang :convert_time_unit (
96- TS , milli_seconds , seconds )},
97- Content = rabbit_basic :build_content (PBasic , <<>>),
98- XName = exchange (VHost ),
99- {ok , Msg } = mc_amqpl :message (XName , Key , Content ),
100- rabbit_queue_type :publish_at_most_once (XName , Msg )
101- end ,
102- {ok , State };
86+ handle_event (# event {type = Type ,
87+ props = Props ,
88+ reference = none ,
89+ timestamp = Timestamp },
90+ # state {protocol = Protocol ,
91+ vhost = VHost ,
92+ has_any_bindings = true } = State ) ->
93+ case key (Type ) of
94+ ignore ->
95+ {ok , State };
96+ Key ->
97+ XName = exchange (VHost ),
98+ Mc = mc_init (Protocol , XName , Key , Props , Timestamp ),
99+ _ = rabbit_queue_type :publish_at_most_once (XName , Mc ),
100+ {ok , State }
101+ end ;
103102handle_event (_Event , State ) ->
104103 {ok , State }.
105104
@@ -207,9 +206,110 @@ key(S) ->
207206 Tokens -> list_to_binary (string :join (Tokens , " ." ))
208207 end .
209208
209+ get_vhost () ->
210+ case application :get_env (? APP_NAME , vhost ) of
211+ undefined ->
212+ {ok , V } = application :get_env (rabbit , default_vhost ),
213+ V ;
214+ {ok , V } ->
215+ V
216+ end .
217+
218+ mc_init (amqp_1_0 , XName , Key , Props , Timestamp ) ->
219+ Sections = [# 'v1_0.message_annotations' {content = props_to_message_annotations (Props )},
220+ # 'v1_0.properties' {creation_time = {timestamp , Timestamp }},
221+ # 'v1_0.data' {content = <<>>}],
222+ Payload = iolist_to_binary ([amqp10_framing :encode_bin (S ) || S <- Sections ]),
223+ Anns = #{? ANN_EXCHANGE => XName # resource .name ,
224+ ? ANN_ROUTING_KEYS => [Key ]},
225+ mc :init (mc_amqp , Payload , Anns );
226+ mc_init (amqp_0_9_1 , XName , Key , Props0 , TimestampMillis ) ->
227+ Props = [{<<" timestamp_in_ms" >>, TimestampMillis } | Props0 ],
228+ Headers = fmt_proplist (Props ),
229+ TimestampSecs = erlang :convert_time_unit (TimestampMillis , millisecond , second ),
230+ PBasic = # 'P_basic' {delivery_mode = 2 ,
231+ headers = Headers ,
232+ timestamp = TimestampSecs },
233+ Content = rabbit_basic :build_content (PBasic , <<>>),
234+ {ok , Mc } = mc_amqpl :message (XName , Key , Content ),
235+ Mc .
236+
237+ props_to_message_annotations (Props ) ->
238+ KVList = lists :foldl (
239+ fun ({K , # resource {virtual_host = Vhost , name = Name }}, Acc ) ->
240+ Ann0 = {to_message_annotation_key (K ), {utf8 , Name }},
241+ Ann1 = {{symbol , <<" x-opt-vhost" >>}, {utf8 , Vhost }},
242+ [Ann0 , Ann1 | Acc ];
243+ ({K , V }, Acc ) ->
244+ Ann = {to_message_annotation_key (K ),
245+ to_message_annotation_val (V )},
246+ [Ann | Acc ]
247+ end , [], Props ),
248+ lists :reverse (KVList ).
249+
250+ to_message_annotation_key (Key ) ->
251+ Key1 = to_binary (Key ),
252+ Pattern = try persistent_term :get (cp_underscore )
253+ catch error :badarg ->
254+ Cp = binary :compile_pattern (<<" _" >>),
255+ ok = persistent_term :put (cp_underscore , Cp ),
256+ Cp
257+ end ,
258+ Key2 = binary :replace (Key1 , Pattern , <<" -" >>, [global ]),
259+ Key3 = case Key2 of
260+ <<" x-" , _ /binary >> ->
261+ Key2 ;
262+ _ ->
263+ <<" x-opt-" , Key2 /binary >>
264+ end ,
265+ {symbol , Key3 }.
266+
267+ to_message_annotation_val (V )
268+ when is_binary (V ) ->
269+ case mc_util :is_utf8_no_null_limited (V ) of
270+ true ->
271+ {utf8 , V };
272+ false ->
273+ {binary , V }
274+ end ;
275+ to_message_annotation_val (V )
276+ when is_integer (V ) ->
277+ {long , V };
278+ to_message_annotation_val (V )
279+ when is_number (V ) ->
280+ % % AMQP double and Erlang float are both 64-bit.
281+ {double , V };
282+ to_message_annotation_val (V )
283+ when is_boolean (V ) ->
284+ {boolean , V };
285+ to_message_annotation_val (V )
286+ when is_pid (V ) ->
287+ {utf8 , to_pid (V )};
288+ to_message_annotation_val (V )
289+ when is_atom (V ) ->
290+ {utf8 , atom_to_binary (V , utf8 )};
291+ to_message_annotation_val ([{Key , _ } | _ ] = Proplist )
292+ when is_atom (Key ) orelse
293+ is_binary (Key ) ->
294+ Map = lists :map (fun ({K , V }) ->
295+ {{utf8 , to_binary (K )},
296+ to_message_annotation_val (V )}
297+ end , Proplist ),
298+ {map , Map };
299+ to_message_annotation_val ([{Key , Type , _ } | _ ] = Table )
300+ when is_binary (Key ) andalso
301+ is_atom (Type ) ->
302+ % % Looks like an AMQP 0.9.1 table
303+ mc_amqpl :from_091 (table , Table );
304+ to_message_annotation_val (V )
305+ when is_list (V ) ->
306+ {list , [to_message_annotation_val (Val ) || Val <- V ]};
307+ to_message_annotation_val (V ) ->
308+ {utf8 , fmt_other (V )}.
309+
210310fmt_proplist (Props ) ->
211311 lists :foldl (fun ({K , V }, Acc ) ->
212- case fmt (a2b (K ), V ) of
312+ case fmt (to_binary (K ), V ) of
213313 L when is_list (L ) -> lists :append (L , Acc );
214314 T -> [T | Acc ]
215315 end
@@ -226,11 +326,8 @@ fmt(K, V) when is_number(V) -> {K, float, V};
226326fmt (K , V ) when is_binary (V ) -> {K , longstr , V };
227327fmt (K , [{_ , _ }|_ ] = Vs ) -> {K , table , fmt_proplist (Vs )};
228328fmt (K , Vs ) when is_list (Vs ) -> {K , array , [fmt (V ) || V <- Vs ]};
229- fmt (K , V ) when is_pid (V ) -> {K , longstr ,
230- list_to_binary (rabbit_misc :pid_to_string (V ))};
231- fmt (K , V ) -> {K , longstr ,
232- list_to_binary (
233- rabbit_misc :format (" ~1000000000p " , [V ]))}.
329+ fmt (K , V ) when is_pid (V ) -> {K , longstr , to_pid (V )};
330+ fmt (K , V ) -> {K , longstr , fmt_other (V )}.
234331
235332% % Exactly the same as fmt/2, duplicated only for performance issues
236333fmt (true ) -> {bool , true };
@@ -241,20 +338,16 @@ fmt(V) when is_number(V) -> {float, V};
241338fmt (V ) when is_binary (V ) -> {longstr , V };
242339fmt ([{_ , _ }|_ ] = Vs ) -> {table , fmt_proplist (Vs )};
243340fmt (Vs ) when is_list (Vs ) -> {array , [fmt (V ) || V <- Vs ]};
244- fmt (V ) when is_pid (V ) -> {longstr ,
245- list_to_binary (rabbit_misc :pid_to_string (V ))};
246- fmt (V ) -> {longstr ,
247- list_to_binary (
248- rabbit_misc :format (" ~1000000000p " , [V ]))}.
341+ fmt (V ) when is_pid (V ) -> {longstr , to_pid (V )};
342+ fmt (V ) -> {longstr , fmt_other (V )}.
249343
250- a2b ( A ) when is_atom ( A ) -> atom_to_binary ( A , utf8 );
251- a2b ( B ) when is_binary ( B ) -> B .
344+ fmt_other ( V ) ->
345+ list_to_binary ( rabbit_misc : format ( " ~1000000000p " , [ V ])) .
252346
253- get_vhost () ->
254- case application :get_env (rabbitmq_event_exchange , vhost ) of
255- undefined ->
256- {ok , V } = application :get_env (rabbit , default_vhost ),
257- V ;
258- {ok , V } ->
259- V
260- end .
347+ to_binary (Val ) when is_atom (Val ) ->
348+ atom_to_binary (Val );
349+ to_binary (Val ) when is_binary (Val ) ->
350+ Val .
351+
352+ to_pid (Val ) ->
353+ list_to_binary (rabbit_misc :pid_to_string (Val )).
0 commit comments