Remote Administration Daemon Developer Guide

Exit Print View

Updated: July 2014
 
 

User Management Example

Object/interface granularity is subjective. For example, imagine an interface for managing a user. The user has a few modifiable properties:

Table 7-1  Example User Properties
Property
Type
name
string
shell
string
admin
boolean

The interface for managing this user might consist solely of a set of attributes corresponding to the above properties. Alternatively, it could consist of a single attribute that is a structure containing fields that correspond to the properties, possibly more efficient if all properties are usually read or written together. The object implementing this might be named as follows:

com.example.users:type=TheOnlyUser

If instead of managing a single user you need to manage multiple users, you have a couple of choices. One option would be to modify the interface to use methods instead of attributes, and to add a "user" argument to the methods, for example:

setUserAttributes(username, attributes) throws UserError
attributes getUserAttributes(username) throws UserError

This example is sufficient for a single user, and provides support to other global operations such as adding a user, deleting a user, getting a list of users and so on. You might want to give it a more appropriate name, for example:

com.example.users:type=UserManagement

However, suppose there were many more properties associated with the user and many more operations you would want to do with a user, for example, sending them email, giving them a bonus and so on. As the server functionality grows, the UserManagement's API grows increasingly cluttered. It would accumulate a mixture of global operation and per-user operations, and the need for each per-user operation to specify a user to operate on, and specify the errors associated with not finding that user, would start looking redundant.

username[] listUsers()
addUser(username, attributes)
giveRaise(username, dollars) throws UserError
fire(username) throws UserError
sendEmail(username, message) throws UserError
setUserAttributes(username, attributes) throws UserError
attributes getUserAttributes(username) throws UserError

A cleaner alternative would be to separate the global operations from the user-specific operations and create two interfaces. The UserManagement object would use the global operations interface:

username[] listUsers()
addUser(username, attributes)

A separate object for each user would implement the user-specific interface:

setAttributes(attributes)
attributes getAttributes()
giveRaise(dollars)
fire()
sendEmail(message)

Note -  If fire operates more on the namespace than the user, it should be present in UserManagement where it would need to take a username argument.

Finally, the different objects would be named such that the different objects could be easily differentiated and be directly accessed by the client:

com.example.users:type=UserManagement
com.example.users:type=User,name=ONeill
com.example.users:type=User,name=Sheppard
...

This example also highlights a situation where the rad server may not want to enumerate all objects when a client issues a LIST request. Listing all users may not be particularly expensive, but pulling down a list of potentially thousands of objects on every LIST call will not benefit the majority of clients.