Project

General

Profile

Directory Access and Customization

Introduction

In FWD, all security, server and user configuration is kept in a directory. Access is provided via a Directory Service component, which exposes a set of APIs as public methods through its front-end. The front-end is how the rest of FWD perceives the FWD directory. A back-end is what implements the persistent storage using one of many possible approaches. The back-end is hidden inside the Directory Service package and is of no interest to the rest of the FWD environment.

Participants

Clients of Directory Service are various FWD server components. FWD applications are legitimate clients of the Directory Service as long as they communicate locally. There is no direct access via the network to the Directory Service or the back-end storage server. The directory service API is available via method call from local server application code. These APIs will be exported for remote access by authenticated FWD nodes via the com.goldencode.p2j.net package. Each client is identified by its security context by the means of the SecurityManager. In this chapter, the terms client and security context will be used as synonyms.

The Directory Service Front-End is the portion responsible for the logical view of the FWD directory. It includes the API implementation the clients use to talk to the Directory Service.

Directory Service Back-End is the portion of the package that maps the logical view of the directory into some kind of persistent storage and maintains the semantics of FWD directory objects.

Client access to the Directory Service Front-End is possible in FWD using a complete low-level API and a work-in-progress, high-level API.

Low-level APIs

The low-level APIs are defined by the DirectoryService class in the com.goldencode.p2j.directory package. This is a singleton class, which has its single instance created on server startup. Access to this instance is done using a DirectoryService.getInstance() static method call. Once a reference to this instance is obtained, access to APIs which enumerate, modify, add, delete directory nodes is possible. Each of these APIs will be described in detail in the following sections of this chapter.

High-level APIs

The high-level APIs are defined by the Directory interface in the com.goldencode.p2j.directory package. Currently, this interface defines only a few APIs which search for a node with a given ID, but it will be expanded to handle all the low-level APIs defined by the DirectoryService class in a cleaner way.

The Directory interface is implemented by the DirectoryServer and NullDirectory classes. The NullDirectory is only a safe placeholder, which can be used when the directory is not yet initialized. The DirectoryServer class is responsible of implementing all the APIs defined by the Directory interface.

To initialize the DirectoryServer, the server startup code will call DirectoryServer.initServer()., which creates the single instance which is ever available of this class. This initializes the current directory API provider and exports the Directory interface as a network server, so that it can be accessed remotely. Later on, when access to high-level APIs is needed, the DirectoryServer.getInstance() call can be used to have access to the single implementation instance.

Definitions

A directory is a hierarchy of typed objects. There is only one instance of a directory. Directory objects form a tree. A node in the tree can be a parent of the same type or another type of node. Siblings do not have to be of the same type.

Types of directory objects are predefined. Object types have names. Types define what information can be stored in an instance of the type, what it is made of, what is mandatory or optional and whether nodes of a type may have children or should always remain leaves.

A directory object is a named set of attributes, each with associated value(s). All attributes have unique names which serve as the keys to values and are of predefined primitive data types. Attribute values may be single or multiple. There is also a type of object without any attributes. For attributes which support multiple values, values consist a set, so each value is unique. The order of the values in the set is undefined.

Every directory object is a node in the directory tree and as such, has an ID. Every node can be identified by a string which describes how to find the object in the tree from its root. The path is made of links separated by '/' character like in file system directories. Every link names a node in the subtree of its parent. Sibling objects all have unique names, although the same names can be reused at different levels of the tree. The root object has the ID of ””. Here are few examples.

  • /security
  • /security/type3/data-abc
  • /security/account_x

Following are the properties which every link name must satisfy:

  • Link names are case-insensitive. That means that object names must differ in more than just string case.
  • Link names can be up to 256 characters in length.
  • Link names may only consist of alpha-numeric characters, dashes, periods and underscores.

Primitive Directory Data Types

The following table lists primitive data types defined in the Directory Service:

Primitive Data Type Java Data Type String Representation Type Name String
integer ATTR_INTEGER int decimal number INTEGER
boolean ATTR_BOOLEAN boolean "true" and "false" BOOLEAN
string ATTR_STRING String character string STRING
double ATTR_DOUBLE double decimal number with a "floating" decimal point DOUBLE
bytearray ATTR_BYTEARRAY byte[] hexadecimal string BYTEARRAY
bitfield ATTR_BITFIELD BitField, a subclass of BitSet which forbids dynamic set growth string of 0s and 1s, enclosed in single quotes followed by 'B' suffix BITFIELD
bitselector ATTR_BITSELECTOR BitSelector, a subclass of BitField which allows only one bit set to 1 at a time string of 0s and 1s, enclosed in single quotes followed by 'B' suffix BITSELECTOR
date ATTR_DATE DateValue, a class which can be converted to and from java.util.Date, with time part ignored and set to 0 string formatted as yyyy-mm-dd DATE
time ATTR_TIME TimeValue, a class which can be converted to and from java.util.Date, with date part ignored and set to 0 string formatted as hh:mm:ss TIME

Directory Object Classes

TBD: add me

Accessing Directory Objects

Access to directory objects can be done using the low-level APIs defined by the DirectoryService class and a few high-level APIs defined by the Directory interface.

Enumerating Objects

Any object in the directory can be a parent for zero or more other objects. Enumerating allows for getting an array of children for a given directory object. It is a simple API:

public String[] enumerateNodes(String nodeId);

This call may return null, which means the specified object is either a leaf node or does not exist, or an array of object IDs, which are relative to the parent object ID. An absolute ID can be produced by concatenating the original nodeID with the '/' character and a relative ID. Relative IDs are just link names.

Security policy may limit visibility of objects. Enumeration always returns only those objects that are visible to the caller according to the current security context.

TBD: examples

Enumerating Attributes

For any given directory object, it is possible to enumerate all attributes by a single API:

public NodeAttribute[] enumerateNodeAttributes(String nodeId);

This call may return null, which means the specified object does not exist, or an array of node attributes. Node attributes are just copies of the attribute records as described in Directory Object Classes section of this chapter, one per existing attribute, completed with the number of values.

Security policy may limit visibility of attributes. Enumeration always returns only those attributes that are visible to the caller according to the current security context.

TBD: examples

Retrieving Attribute Values

There is a set of APIs to retrieve a value of an attribute, one per primitive data type of the attribute. They all differ only in the returned data type. All the defined APIs, for each data type, are defined using this syntax:

public <type> getNode<type name>(String nodeId, String name, int index);
public <type>[] getNode<type name>(String nodeId, String name);
public <type> getNode<type name>(String nodeId, String name);

where <type> is the type of the attribute being searched and <type name> is the name of the type as used in the API. All these APIs will be explained in detail in this section.

As can be seen, the first API returns a single value by its index, whereas the second API returns the whole set as an array. Both may return null, which means the specified object or attribute does not exist. Third method is a convenience method for first one with index parameter set to 0.

There is also a single API that returns all attributes with their values in one call:

public Attribute[] getNodeAttributes(String nodeId);

This method returns null if the referenced object does not exist or has no attributes. Otherwise it returns an array of Attribute objects, which provide the following methods:

public NodeAttribute getDefinition();
public int getCount();
public Object getValue(int index);

With these methods one can query the attribute name and type, find out the number of values and query any particular value of the attribute. The value is returned as a generic object but the application can cast it to its primitive data type.

TBD: high level APIs to get node values

TBD: examples

TBD: list of all APIs, per data type

public Integer getNodeInteger(String nodeId, String name, int index);
public Integer[] getNodeIntegers(String nodeId, String name);
public Integer getNodeInteger(String nodeId, String name);

Editing Directory Objects

There are few things to keep in mind when using the Directory Service for editing the directory:

  • editing operations should be issued within an editing session;
  • only one editing session can be opened per security context at a time;
  • by opening an editing session, the calling client locks a specified branch of the directory tree;
  • multiple clients can do simultaneous editing as long as they work with different branches of the directory tree;
  • all queries on the locked directory branches are possible;
  • simultaneous editing is synchronized by the Directory Service;
  • the back-end implementation may pose some limitations on the availability of an operation against a particular object;
  • the calling code should be prepared to face two kinds of error returns:
    • errors due to restricted access to specific directory objects;
    • errors due to back-end limitations like, for instance, read-only objects.

The following sections introduce the methods available for doing elementary edits (which cannot be executed outside of a batch session). The final section of this chapter discusses the batch edit feature.

Modifying Attribute Values

There is a set of APIs to set a value of an attribute, one per primitive data type of the attribute. They all differ only in the parameter data type. Here are sample APIs to add and set an integer attribute value:

public boolean setNodeInteger(String nodeId, String name, int index, int value);

public boolean addNodeInteger(String nodeId, String name, int value);

public boolean setNodeIntegers(String nodeId, String name, int[] value);

The first API sets a single value by its index. The third API is a variation that replaces all existing values with those specified in array. The second API just adds a new value to the existing set. All return true if operation succeeds. The attribute name should be one of the defined ones for this directory object class.

There is also a single API that replaces all existing attributes with a new set of attributes with their values in one call:

public boolean setNodeAttributes(String nodeId, Attribute[] data);

The array of attributes for this call can be either obtained by calling getNodeAttributes or constructed as in "the sample code":#constructing_attributes below illustrating object creation. The new set of attributes should meet the requirements of the object class, namely, all mandatory attributes must be present in the array or the call fails.

To delete a single value of an attribute or the whole attribute, there are type-independent APIs:

public boolean deleteNodeAttributeValue(String nodeId, String name, int index);

public boolean deleteNodeAttribute(String nodeId, String name);

If an attribute only has one value, the deletion of that sole value deletes the whole attribute. Both calls fail if they would cause a mandatory attribute to be deleted, which is not allowed.

Modifying Objects

To create an object, application calls an API and provides:

  • absolute object ID;
  • object class name;
  • array of Attributes.

The requirements is to provide all mandatory attributes with their values. The call fails if it is not met. This is the API:

public boolean addNode(String nodeId, String class, Attribute[] data);

For instance, to create an object of a class "namelist" that has attributes:

  • time from, mandatory, single;
  • string list, mandatory, multiple;

under the name "/groups/group_A", the application could do the following:

// Let's assume NodeAttribute na1 and na2 were obtained previously

// na1 describes the ATTR_TIME attribute named "from"

// na2 describes the ATTR_STRING attribute named "list"

// create the 1st attribute

Attribute attr1 = new Attribute(na1, new Time());

// create the 2nd attribute

Attribute attr2 = new Attribute(na2, new String[] {"a", "b", "c"});

// create an array of attributes

Attribute[] data = new Attribute[2];

// put attributes into the array

data[0] = attr1;

data[1] = attr2;

// create a new directory object

boolean result = addNode("/groups/group_A", "namelist", data);

The code above creates a new object which can be referenced as "/groups/group_A", having an attribute of type time named "from" with a single value, and an attribute of type string named "list" with 3 values: "a", "b", and "c".

To delete an object, use this API:

public boolean deleteNode(String nodeId);

This call fails if the referenced object has children objects. Those have to be deleted first.

Batch Editing Feature

Batch editing feature implemented in the Directory Service protects the users of the FWD directory from seeing partial updates being made to the directory. This feature enforces some discipline when editing the directory.

First of all, the application should open an editing session by calling:

public boolean openBatch(String nodeId);

This call specifies a branch of the directory that should be considered busy for the lifetime of the editing session. The Directory Service uses internal batch editing lock manager to lock the specified branch and refuses request if it tries to lock a branch which is already locked by other client.

The lock only blocks parallel attempts to edit the same subtree. All regular queries of attributes are possible. Once the lock has been obtained and an editing session opened, the application is allowed to call editing methods discussed in previous sections. These calls do not have any immediate effect on the back-end, though. The Directory Service simply batches them internally. Therefore, the client which opened particular editing session sees changes as if they are already committed while other clients see unchanged directory.

Eventually, the application is done with editing. It calls:

public boolean closeBatch(boolean disposition);

The disposition can be either commit the session, or discard all changes. Discarding changes is as easy as simply destroying the batch. Committing the session is done as follows:

  • a backup copy of all objects/attributes that are about to be changed is made;
  • a boolean attribute of the lock object is set to indicate the backup is available;
  • a string attribute of the lock object is set to object ID of the backup;
  • batched changes are applied to the directory objects; this is the only period of time when the subtree is not available for queries and the application would block until the update is done;
  • the lock object is deleted from the directory.

This method provides collision detection and crash protection. At startup, the Directory Service scans the directory and discovers all existing lock objects created by the same server. The only explanation of their existence is the system crash. The locked branch of the directory may need to be backed out. This is the logic:

  • the "backup available" attribute is checked;
    • if it is set, then the system crashed while applying changes and definitely needs backing out:
      • back out is made using the existing backup;
      • the lock object is deleted from the directory;
      • the backup information is deleted.
    • if it is not set, the system crashed while making backup;
      • no backing out is necessary;
      • the lock object is deleted from the directory;
      • the partial backup information that might have been created is deleted.

The implementation locates the backup by retrieving string attribute of the lock object. Also, multiple simultaneous locks put on different tree branches, require that multiple backup sets coexist.

GES: This chapter should describe the following:

- Brief summary of how the directory works, why it is useful, what is in it, the data types and so forth. The com/goldencode/p2j/directory/package.html should be pretty helpful here.

- How to read and search values from the directory. There are 2 API levels, the clean/simple Directory interface and the old-style (soon to be deprecated) low level API. The new API is incomplete, so the old API still needs to be used for things like enumerations/searching or for certain data types. Both should be explained thoroughly, so that someone can properly write directory “client” code. Make sure to document how to read/search for all the different standard data types.

- How to write values to the directory. Only the low level API exists today so that is what will have to be documented. A better new API will be provided in the future. This should also be thoroughly handled, including how to write all the different data types.

- Brief description of the dependencies upon the security being setup properly so that the caller can read and/or search and/or write the portions of the directory that are of interest. Make sure to reference the sections in the Admin guide that detail these ACLs and requirements.

- How to extend the directory with your own custom types. Describe why you would do this (the pros and cons). Fully document the syntax for the extention XML file and show some examples. Explain how the server finds the extention file and how to get it into the right place in the jar using the build XML.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.