1%% This Source Code Form is subject to the terms of the Mozilla Public 2%% License, v. 2.0. If a copy of the MPL was not distributed with this 3%% file, You can obtain one at https://mozilla.org/MPL/2.0/. 4%% 5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6%% 7 8-module(rabbit_exchange_decorator). 9 10-include_lib("rabbit_common/include/rabbit.hrl"). 11 12-export([select/2, set/1]). 13 14-behaviour(rabbit_registry_class). 15 16-export([added_to_rabbit_registry/2, removed_from_rabbit_registry/1]). 17 18%% This is like an exchange type except that: 19%% 20%% 1) It applies to all exchanges as soon as it is installed, therefore 21%% 2) It is not allowed to affect validation, so no validate/1 or 22%% assert_args_equivalence/2 23%% 24%% It's possible in the future we might make decorators 25%% able to manipulate messages as they are published. 26 27-type(tx() :: 'transaction' | 'none'). 28-type(serial() :: pos_integer() | tx()). 29 30-callback description() -> [proplists:property()]. 31 32%% Should Rabbit ensure that all binding events that are 33%% delivered to an individual exchange can be serialised? (they 34%% might still be delivered out of order, but there'll be a 35%% serial number). 36-callback serialise_events(rabbit_types:exchange()) -> boolean(). 37 38%% called after declaration and recovery 39-callback create(tx(), rabbit_types:exchange()) -> 'ok'. 40 41%% called after exchange (auto)deletion. 42-callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 43 'ok'. 44 45%% called when the policy attached to this exchange changes. 46-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> 47 'ok'. 48 49%% called after a binding has been added or recovered 50-callback add_binding(serial(), rabbit_types:exchange(), 51 rabbit_types:binding()) -> 'ok'. 52 53%% called after bindings have been deleted. 54-callback remove_bindings(serial(), rabbit_types:exchange(), 55 [rabbit_types:binding()]) -> 'ok'. 56 57%% Allows additional destinations to be added to the routing decision. 58-callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> 59 [rabbit_amqqueue:name() | rabbit_exchange:name()]. 60 61%% Whether the decorator wishes to receive callbacks for the exchange 62%% none:no callbacks, noroute:all callbacks except route, all:all callbacks 63-callback active_for(rabbit_types:exchange()) -> 'none' | 'noroute' | 'all'. 64 65%%---------------------------------------------------------------------------- 66 67added_to_rabbit_registry(_Type, _ModuleName) -> 68 [maybe_recover(X) || X <- rabbit_exchange:list()], 69 ok. 70removed_from_rabbit_registry(_Type) -> 71 [maybe_recover(X) || X <- rabbit_exchange:list()], 72 ok. 73 74%% select a subset of active decorators 75select(all, {Route, NoRoute}) -> filter(Route ++ NoRoute); 76select(route, {Route, _NoRoute}) -> filter(Route); 77select(raw, {Route, NoRoute}) -> Route ++ NoRoute. 78 79filter(Modules) -> 80 [M || M <- Modules, code:which(M) =/= non_existing]. 81 82set(X) -> 83 Decs = lists:foldl(fun (D, {Route, NoRoute}) -> 84 ActiveFor = D:active_for(X), 85 {cons_if_eq(all, ActiveFor, D, Route), 86 cons_if_eq(noroute, ActiveFor, D, NoRoute)} 87 end, {[], []}, list()), 88 X#exchange{decorators = Decs}. 89 90list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. 91 92cons_if_eq(Select, Select, Item, List) -> [Item | List]; 93cons_if_eq(_Select, _Other, _Item, List) -> List. 94 95maybe_recover(X = #exchange{name = Name, 96 decorators = Decs}) -> 97 #exchange{decorators = Decs1} = set(X), 98 Old = lists:sort(select(all, Decs)), 99 New = lists:sort(select(all, Decs1)), 100 case New of 101 Old -> ok; 102 _ -> %% TODO create a tx here for non-federation decorators 103 [M:create(none, X) || M <- New -- Old], 104 rabbit_exchange:update_decorators(Name) 105 end. 106