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

Compare with Current View Page History

« Previous Version 14 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

All 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.
  • In general preferences will be updatable/deletable by the Principal that owned it.
  • Special users will be able view/update/delete the preferences of another user.   Users with this permission will normally belong to an identity maintainer role.

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 that are visible to the authenticated user.  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.

Users in a possessing a special super permission, will see all the preferences of the object or objects, regardless of visibility. 

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.

The preference will always belong to the authenticated user.

POST /api/latest/virtualhost/myvhn/myvh/preferences
{
 type: "QUERY",
 name: "mypref",
 description: 'Acme Hot queues',
 visibleTo: ldap.mycompany.org:operators,
 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,
 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.

The update will be rejected if the authenticated user does not match the preference's owner. The API will reject an attempt to update the preference's owner.

 

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

Delete

Preferences can be deleted with a DELETE against the full URL.

The delete will be rejected if the authenticated user does not match the preference's owner, unless the user holds the preference maintainer permission.

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

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

Preference

 Representation in storage

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

{
 id: <UUID - immutable>,
 name: <name - immutable>,
 type: <type discriminator>
 description: <description>,
 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>],
 <arbitrary preference specific name/value paird>

}

Preferences Model Object

At runtime, a Java object will represent the Preference.    The interface for this class will look like this.  Specialisation of the interface will support the different preference types.  The implementations will be backed by a map which will store the name (string) value (object) associations.

(At the moment, I am not imagining analogues of @ManagedAttribute etc).

 

public interface Preference
{
 UUID getId();
 String getName();
 String getType();
 String getDescription();
 Principal getOwner();
 List<Principal> getVisibleTo();
  Map<String,Object> getAttributes();
 void setAttributes(Map<String,Object>); 
 void delete();
}

Preference#getAttributes

Returns a r/o map containing the attributes of the preference.  Used by the REST API and the Store.

Preference#setAttribute

This will call out to the security manager to authorise the update of the preference.

It then updates the map backing the preference object and invoke the store listener to cause the store to update the preference.

Preference#delete

This will call out to the security manager to authorise the update of the preference. 

It the cause the associated configured object to remove the preference and invoke the store listener to cause the store to delete preference.

Configured Object

All ConfiguredObjects will have a set of preferences, which may be empty.  These are the preferences associated with the configured object.  The configured object will also have a factory to support the creation of new preferences and finder methods to allow preferences to be looked-up.

 

public interface ConfiguredObject
{
  Preference createPreference(Map<String,Object>);
  Set<Preference> getPreferences();
  Preference getPreferenceById(UUID);
  Preference getPreferenceByName(String);
}

ConfiguredObject#createPreference

This will call out to the security manager to authorise the creation of the preference.

It will then use the PreferencesFactory to create the new preference.  The factory will be responsible for registering the preference with the configured object to which it is associated.

ConfiguredObject#getPreference*()

This will call out to the security manager to authorise the access of the preference(s).  

Preferences Factory

There will be a preference factory which will work rather like the object factory.  The Model will give the ConfiguredObject its preference factory.  BrokerModel will have a hard relationship with a PreferencesFactoryImpl which will know how to lookup specific preference factories and create preference objects.   We might automate the creation of preference factories rather like we do for configure objects.

Security Manager

The security manager will gain responsibilities for authorising preference operations.

  • authoriseCreate(Preference new)
  • authoriseUpdate(Preference existing, Preference new)
  • authoriseDelete(Preference)
  • authoriseAccess(Preference)

For the moment the normal user operations, the existing ACL rules CONFIGURE BROKER and ACCESS VIRTUALHOST will be used as stop gaps.

For security manager will be responsible for ensuring that only the owner of the preference updates or deletes the preference.  It will also ensure that the user views only preference  that the user owns or has visibility of owing to group membership.

For the super-user, we will need a new permission will probably be required.

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 stores.

  • 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.

Recovery

At recovery time the AbstractSystemConfig and AbstractVirtualHostNode will need to read all the preferences from their respective stores and create Preference objects.    This needs to occur after the configured objects are recovered. In this phase, objects corresponding to the records must be created.  The recovery algorithm will be similar to that of configured object recovery as preferences may reference other preferences, so some preferences (Dashboards) may have to await the creation of others before they may be created.   As preferences are created they need to be assigned to their associated configured object.

If during the recovery phase, a preference is encountered that refers to a configured object that no longer exists, the preference can be dropped.  If a preference is found that refers to a preference that no longer exists, the reference can be removed.  The latter would happen if a dashboard of user B refers to a query of user A.  If user A deletes their query, user B's reference will be left dangling.  On next recovery, the dangling reference must be removed.  (The UI should probably just display a ? where the query would have been).

Persistence

The persistence stores need to listen for new preferences, updates to existing preferences or deletes.  A listener mechanism similar to ConfigurationChangeListener will be used.

 

REST API/Object Model interaction

The REST API needs to lookup configured object referred to by the path.  The path must uniquely identify a single object otherwise an error must result.

For create: Once the configured object is identified, the REST API must call CO#createPreference() passing the map.

For update: Once the configured object is identified, the REST API must call CO#getPreferenceById|Name() passing the identifier.  If the preference does not exist, return a 404. Once the preference is identified, call Preference#setAttributes() to update the preference's state.

For delete: Once the configured object is identified, the REST API must call CO#getPreferenceById|Name() passing the identifier. Once the preference is identified, call Preference#delete()

For retrieve, : Once the configured object is identified, the REST API must call CO#getPreferences()

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? 

 

TO UPDATE:

preferences

 

 

  • No labels