You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

The Java Broker requires a mechanism that allows a preference, which is owned by a Principal,  to be associated with an arbitrary object with the object hierarchy.  An example of a preference could be:

  • a query
  • a chart
  • a preferred timezone
  • a dashboard layout

Preferences will be owned by a Principal.  Preference will have a visibility which will allow the preference to be private, or shared amongst a group.  Preferences will be editable only by the Principle that created it.  The UI will allow an operator to clone a copy of a preference of another which is visible to him, in order that he may change his own copy.

Preferences will be durable and thus survive a Broker restart.

Preferences associated with a VirtualHost or below need to automatically propagated automatically amongst the nodes within a HA group.  As an operator, this means I will be able to create, say, a query against my HA virtualhost and the same query will be available to me no matter where the mastership resides.

Unlike relationships between CO, preferences are loosely associated with model objects.  This means that an operator will be able to remove, say a queue, even if preferences are still associated with it, be they belong to the operator himself or another Principal. In this case, the preference becomes orphaned. Orphaned preferences will be automatically purged by the system.

Some preferences may refer to other preferences, for example, a dashboard preference may refer to one or more query or chart preferences.

For some preference types, a unlimited number of instances may be associated to a single configured object, for example, QUERY. For other preference types, the cardinality will be zero or one.  An example will be Timezone.

Preferences need to be extensible and allow for the introduction of further preference types  The storage mechanism needs to be versioned so that an automated upgrader can translate an older to a newer one.

By convention, we will associate preferences with either the Broker or Virtualhost.  The model will permit, preferences to be associated anywhere, but we don't anticipate exploiting this at the moment.

Some other features may 'understand' preferences natively.  An example would be the query API, it may be adapted to allow the caller to invoke a query described by a preference directly, without the need for the client side to retrieve the query and then submit it back to the server for execution.  

High level design

Preference API

The Preference API will be exposed over REST and must be part of the versioned API.  To address the preferences the caller will simply append /preferences as the trailing part of the request.  As a consequence, no configured object may have an operation called preferences.

The basic form of a preferences URL will be:

/api/version/<configured object>/path/to/configured/object/preferences
 

Retrieve

GET will retrieve the preferences associated with the identified object or objects only.  It will never return the preferences associated with a parent or children. Preferences may be filtered by type.

The response will be 200 unless:

  • The exactly identified configured object does not exist (response 404)
  • The exactly identified preference (by name or id) does not exist (response 404)

If no preferences are associated with the object (or objects) the response will be an empty JSON list unless the request indicated an exactly identified preference.

Retrieving one preference by id or name:

GET /api/latest/broker/preferences?id=75d5d2a4-0573-11e6-b512-3e1d05defe78
GET /api/latest/broker/preferences/myquery1

Retrieving all preferences associated with a single configured object:

GET /api/latest/broker/preferences
GET /api/latest/virtualhost/myvhn/myvh/preferences

Retrieving all preferences associated with all configured objects of a particular category

Illustrates all queues and all queues beneath a particular virtualhost

GET /api/latest/queue/*/*/*/preferences
GET /api/latest/queue/myvhn/myvh/*/preferences

Retrieving preferences filtered by type

GET /api/latest/virtualhost/myvhn/myvh/preferences?type='QUERY'

Create

Preferences can be created with a PUT request against full URI (one ending with the new preference's name) or submitting a PUT or POST against the parent URI.

POST /api/latest/virtualhost/myvhn/myvh/preferences
{
 type: "QUERY",
 name: "mypref",
 description: 'Acme Hot queues',
 visibleTo: ldap.mycompany.org:operators,
 detail : {
  select: 'id,name,queueDepthMessages'
  where: 'queueDepthMessages > 1000'
 }
}
 
HTTP/1.1 201 Created
Location: /api/latest/preferences/mypref
POST /api/latest/virtualhost/myvhn/myvh/preferences/mypref
{
 type: "QUERY",
 description: 'Acme Hot queues',
 visibleTo: ldap.mycompany.org:operators,
 detail : {
  select: 'id,name,queueDepthMessages'
  where: 'queueDepthMessages > 1000'
 }
}
 
HTTP/1.1 201 Created
Location: /api/latest/preferences/mypref

Update

Preferences can be updated with a PUT/POST request against full URI (one ending with the preference's name).   To fit in with the behaviour current API, a partial update will be supported until we change this globally to require that the client returns the complete status.

 

POST /api/latest/virtualhost/myvhn/myvh/preferences/mypref
{
 type: "QUERY",
 description: 'Acme Hot queues',
 visibleTo: ldap.mycompany.org:operators,
 detail : {
  select: 'id,name,queueDepthMessages'
  where: 'queueDepthMessages > 1000'
 }
}
 
HTTP/1.1 200 OK

Delete

Preferences can be deleted with a DELETE against the full URL

DELETE /api/latest/virtualhost/myvhn/myvh/preferences/mypref

DELETE /api/latest/virtualhost/myvhn/myvh/preferences/?id=75d5d2a4-0573-11e6-b512-3e1d05defe78

 

 

 

Preference Object

Representation in storage

In the store an instance of a Preference will look like the example below. 

{
id: <UUID>,
type: <type of the preference - immutable>,
associatedId: <UUID of model object - immutable>,
owner: <Domain prefixed Principal - normally that belonging to a user - immutable>,
visibleTo: [<Domain prefixed Principal - normally that belonging to a group>],
<type specific attributes>
}

Preferences Object

At runtime, a Java object will represent the Preference.  It will implement an interface that looks like this.  The reference to the associated ConfiguredObject will be resolved as the preferences are recovered from the store.

The preferences object will have a cardinality annotation.  This will allow preferences that have cardinality at most one to be flagged that way.

 

@cardinality=AT_MOST_ONE|UNLIMITED
public interface Preference
{
//  Immutable
UUID _id;
// Immutable
ConfiguredObject _associatedObject;
private String _name;
private String _description;
Principal owner;
List<Principal> visibleTo;
}

Preferences Root

A PreferenceRoot object be responsible for keeping a reference to the preferences once they are recovered, allowing preferences to be conveniently accessed.  It will also be the factory that will created.

The management layer will use this object.  We might auto generate factories for preferences in a similar way as we already do for configured objects.

The Broker and AbstractVirtualHost will both have a PreferencesRoot.


Preference Store

We will have a PreferenceStore interface rather like DurableConfigurationStore

  • open/close
  • upgrade
  • visit
  • create
  • update
  • delete

API is in terms of PreferencesRecord (analogous to ConfiguredObjectRecords).  The stores themselves will hold the preference records as JSON, but this isn't part of the contract.

AbstractSystemConfig and AbstractVirtualHostNode have a separate preference store.

  • The concrete instances of SystemConfig and VirtualHostNode responsible for creating the preference store too.
  • For storage mechanism BDB, JDBC, Derby there would be a restriction that the preference store must be co-located with the configuration.
  • For storage mechanism JSON the preferences would be stored in a separate file.

Probably need a PreferenceStoreProvider interface.

REST API

REST API would be part of the versioned API.

URLs would be of the form: /api/latest/preferences/

The caller won't know if the preference is stored in the broker or virtualhostnode preference store.

Create

 The following will create a QUERY preference belonging to the currently logged on user, attached to the given UUID.  Internally the Broker must find the associated UUID (which could be anywhere in the Configured Object tree) and then navigate back up the tree to find the first point that is capable of storing preferences.

POST /api/latest/preferences/
{
type: QUERY,
description: 'Acme Hot queues',
associatedId: 75d5d2a4-0573-11e6-b512-3e1d05defe78,
visibleTo: ldap.mycompany.org:operators,
select: 'id,name,queueDepthMessages'
where: 'queueDepthMessages > 1000'
}
 
HTTP/1.1 201 Created
Location: /api/latest/preferences/?id=81dfeb5c-0573-11e6-b512-3e1d05defe78
Update

The following will update that QUERY preference with the given UUID.  In a similar way to Create, the Broker will need to traverse the tree to find the associated UUID in order to know if the preference is stored at the Broker or Virtualhost level.

PUT /api/latest/preferences/?id=81dfeb5c-0573-11e6-b512-3e1d05defe78
{
type: QUERY,
description: 'Acme Hot queues',
associatedId: 75d5d2a4-0573-11e6-b512-3e1d05defe78,
visibleTo: ldap.mycompany.org:operators,
select: 'id,name,queueDepthMessages,oldestMessageAge'
where: 'queueDepthMessages > 750'
}
 
HTTP/1.1 200 Ok

Delete

Similar to UPDATE - will permit the deletion of a single preference owned by this user.

Get

Get will return either a single preferences object (id=...), or all/some the preferences belonging to or visible to the authenticated principal.   We might want to consider a type filter (type=QUERY , type=TIMEZONE etc) so that preferences of a particular type are returned.

GET /api/latest/preferences/?id=81dfeb5c-0573-11e6-b512-3e1d05defe78
GET /api/latest/preferences/?type=QUERY

How do I find the preferences associated with a particular virtualhost?  We could support a query like this:

GET /api/latest/preferences/?associatedId=75d5d2a4-0573-11e6-b512-3e1d05defe78

Persistence/Recovery

At recovery time (Broker or Virtualhostnode), the preferences need to be read from the store and objects Preference objects created.   This should occur after the configured objects have been recovered.  The purpose of this phase is to resolve the associated configured object ids and references to any other preference objects (as wiil be the case in dashboard layouts).  For this reason, a similar recovery algorithm to configured object recovery may be required.  The recovery phase can probably be relaxed about dangling references.  If a preference refers to a configured object that no longer exists, the preference can be dropped.  If a preference (say a dash board preference) refers to another preference (say a query reference), and the query reference no longer exists, the reference will need to be dropped (made null).  In this case, I think we'd want the dash board to display a ? in the place of the broken widget, but the dashboard should not fail.

Some object in the system will need to hold a reference to the PreferenceRoot so that Management is able to expose them via the REST API.  It seems like this responsibility needs to live with the Broker and the VirtualHostNodes.

Creating a New Preference

The management API will need to get a reference to the appropriate PreferenceRoot and call the create method on it to create the preference.

Updating a Preference

The management API will need to get a reference to the appropriate Preference, then call its update method which will work in a similar way to ACO#setAttributes().  The store needs to listen for changes and make the appropriate update to the store.

 

Open Questions

  1. Should a super user be able to view/change/delete the preferences of another user?
  2. How will the preferences of belonging to users who leave an organisation be facilitated?
  3. Can a user associate a preference with an object that they don't have permission to access? What happen if permissions on an object become more restrictive after the preference is created.
  4. Should preferences have metadata?  At the moment I think we can live without it
  5. How will preferences map to AMQP Management?
  6. Should context variables be interpolated into a preference's values? 

 

preferences

 

 

  • No labels