Current situation
Currently the broker is configured via a config.xml. This file is read on startup and the system responds by creating objects that form the Broker. The current system has a number of shortcomings:
- the config.xml is poorly structured and inconsistent
- the approach taken to producing the objects from the configuration is poorly structure. This makes the Broker difficult to change and test.
- the config system is such that there is no ability to change the configuration at runtime (from the management layer). For instance it is currently very hard to support the dynamic addition and removal of virtualhosts at runtime. This means the user currently has to hand-edit a complex xml document and restart the Broker.
Brief Overview of Broker Architecture
The Broker currently consists of a tree of ConfiguredObjects, each representing a functional-area of the Broker that the user may wish to manage. The Broker is the top of the tree and has children such as Ports, AuthenticationManagers etc. In addition to children, configured objects also have a series of attributes. For instance, a Port has a portNumber attribute.
This tree is created on Broker start-up from Broker configuration.
Design Proposal
The main features of the design are as follows:
- Introduce a ConfigurationEntry class and ConfigurationEntryStore interface to abstract the Broker from the details of how the configuration is stored. This will allow the configuration to be stored in different stores (flat-file, Derby, Oracle-BDB, SQL, etc) and remove the hard dependencies on Commons Config.
- Allow for changes to be made dynamically (through management interfaces) to be reflected immediately in the stored configuration. This will do away with the need for the user to hand-edit configuration files.
- Improve the mechanism used to construct the Broker from its configuration.
Key Interfaces
ConfigurationEntry
POJO holding the configuration for the ConfiguredObject. It also has a reference to the ConfigurationEntryStore in order to be able to load its children configuration entries.
ConfigurationEntryStore
The ConfigurationEntryStore is responsible for reading and saving of ConfigurationEntries.
Command line arguments and default settings should be also represented as ConfigurationEntryStores. The CompositeConfigurationEntryStore will be responsible for the loading and merging of the configuration from underlyaing stores (command line arguments store, persistent configuration store and default settings)
BrokerCreator and ConfiguredObjectCreators
The BrokerCreator constructs the tree of the ConfiguredObjects representing the Broker from the ConfigurationEntry. It will delegate the creation of broker children (VirtualHosts, AuthenticationProviders, Ports etc) to their own Creators. Also BrokerCreator is responsible for setting of "parent-child" relationships between Broker and its children configured object.
ConfigurationChangeMonitor
Has three responsibilities:
- given a ConfiguredObject, it registers itself as a ConfigurationChangeListener with the ConfiguredObject and its supported children (and descendents).
- as more supported children are added to the tree, if must register itself with the new child so that any future updates to the child are heard.
- cause changes to be reflected in the store by using the ConfigurationEntryStore
The ConfigurationChangeMonitor will avoid listening for events of children of VirtualHosts, nor will it register itself with children beneath the virtualhosts within the tree.
UML
The following UML diagrams document the design.
Class Diagram
Sequence Diagram - Broker Startup
Sequence diagram depicting Broker start-up, loading of configuration, the forming of the ConfiguredObject tree
and the registering of listeners to hear changes/updates.
Sequence Diagram - Adding a new child from the management interface
Sequence diagram depicting the adding of the new child from the management interface. This shows how the new child is persisted into the store, and how the ConfigurationChangeMonitor registers itself with the new child.
Sequence Diagram - Changing of attributes from management interfaces
Json configuration store and Json configuration format.
On first stages (until ConfiguredObject relationships are introduced) the ConfiguredObject will be stored in the stores using ConfigurationEntry representation. Thus all attributes including references to the other ConfiguredObjects (which are not children) will be stored in the attributes and children will stored as hierarchy.
For example, the Port will be stored in JSON format as follows
{ id: "c7038ed9-9ba9-4db1-bc56-ab6c3ab27273", type: "Port", attributes: { port: 5671, transport: "SSL", bindingAddress: "0.0.0.0", protocols: [ "AMQP1-0", ... ], socketReceiveBuffer: 262144, // optional socketSendBuffer: 262144, // optional defaultSupportedProtocolReply: "AMQP0-9" // optional, tcpNoDelay: true, // optional frameSize: 65536, // optional trustStore: "c7038ed9-9ba9-4db1-bc56-ab6c3ab27279", keyStore: "c7038ed9-9ba9-4db1-bc56-ab6c3ab27278", clientCertificateAuthType: "NEED" //NEED|WANT|NONE, } }
In the snippet above, the port is declared with 12 attributes. The attributes "trustStore" and "keyStore" are actually point to the configured objects having IDs "c7038ed9-9ba9-4db1-bc56-ab6c3ab27279" and "c7038ed9-9ba9-4db1-bc56-ab6c3ab27278" accordingly. The "type" field carries the string identifier of the model interface. It will be used by the ConfiguredObjectFactory to find the interface class in order to invoke createChild method on a Broker object.
The full example of broker JSON configuration is provided here.