JA-SIG
Home
About uPortal
Documentation
Getting Started
Developers
Implementors
Users
Background
Release
|
Developers and Deployers Guide to the Composite Group Service
Contents
Introduction
What's In This Document?
Goals and
Rationale of the Composite Service
The Service Design
Groups, Keys and
Service Names
Assembling the Composite
Configuring the Composite
Caching of Group Members
Alternate Group Stores
Next Steps
Introduction
Early in the uPortal project, it became clear that some
mechanism was needed for grouping portal users, chiefly for the
purpose of authorization. The org.jasig.portal.groups
package evolved in response. It defines an api for managing groups
of portal entities such as IPersons and ChannelDefinitions.
The groups framework does not actually operate on IPersons or
ChannelDefinitions; it manipulates stub objects whose keys and types
point to the underlying entities. The stubs are implementations of org.jasig.portal.groups.IEntity ,
and their only concern is their group memberships. A stub knows
nothing about its underlying entity except its key and type. The
groups it belongs to are implementations of org.jasig.portal.groups.IEntityGroup .
Groups are recursive (groups can contain other groups) and homogeneous
(their IEntities have only one type of underlying entity.)
Prior to version 2.1, groups came from a group service with a
single store. As of version 2.1, uPortal ships with a composite
group service made up of multiple component services, each with its own
group store. This document describes the composite design and the
steps involved in configuring a composite group service. It is
principally aimed at implementors and developers, and to a lesser
extent, at planners evaluating uPortal's support for native sources of
group information.
What's In This Document?
This document is organized as follows: Goals and Rationale of
the Composite Service sets out the argument for a composite
service. The next section (The
Service Design) describes the service api and the service class
hierarchy. This is followed by a discussion of group keys and
their relationship to the composite design (Groups, Keys and Service Names)
. These first 3 sections are aimed at developers. If you
are interested only in deploying a composite service, you can skim them
or skip them entirely. The next 4 sections are aimed at
deployers. They describe the process of composite service assembly
(Assembling the Composite), the
configuration file that controls the assembly process (Configuring the Composite),
configuration issues involving caching (Caching of Group Members) and the
design of the 3 alternate group stores that ship with uPortal 2.3 (Alternate Groups Stores). The
last section (Next Steps) presents a very
general outline for getting started with groups.
Unless otherwise noted, all referenced classes are in the org.jasig.portal.groups
package. I'm assuming some familiarity with the basic groups
types, IGroupMember , IEntity and IEntityGroup ,
and with the service façade, org.jasig.portal.services.GroupService ,
which have changed little since uPortal 2.0. For more information
on these types, see The
Developers Guide to Groups or javadoc for org.jasig.portal.groups .
The following terms are used interchangeably: IEntityGroup and
group; IGroupMember and group member; composite group service and
service. Depending on the context, entity may refer to an IEntity
or to the underlying entity that is referred to by the IEntity .
Goals and
Rationale of the Composite Service
Many institutions have group information that is not under the
control of the portal, for example, in an LDAP server. In some
organizations, this information is spread over a number of external
sources. In order to use it, the portal must be able to combine
group data from multiple sources and adapt it to the portal groups
design. For example, if LDAP is one of the sources, the portal
needs an LDAP adaptor that
makes LDAP attributes look like portal group memberships. These
memberships, supplemented by memberships stored in the reference portal
database, can be associated with Permissions.
In this way, portal authorization can be driven from LDAP and managed
from within the portal.
The composite groups system is a framework for creating and
managing group adaptors. Its job is to aggregate group information
from a variety of sources and present it in a consistent form to clients
who can remain unaware of its origins. In fact, a group service
client never interacts directly even with the composite group
service. A client makes a request to the service façade to
obtain a group member, and the group member acts as an entry point
into the composite group system. Once the client has a reference
to a new or pre-existing group member, it makes subsequent requests to
the group member itself, and from then on it can ignore the service of
origin of any group member it navigates to.
The Service Design
The Service Hierarchy. The composite group service
api is divided among 3 service types, IComponentGroupService ,ICompositeGroupService
and IIndividualGroupService , each of which defines
responsibilities for a specific service role. An IComponentGroupService
is a composite component. It is concerned with
composite service assembly and with identifying components in the
composite. The ICompositeGroupService represents the
composition as a whole, encapsulating service components and
delegating requests for group services. The IIndividualGroupService
defines responsibilities for a specific group service that reads and
possibly writes groups. It is the leaf component in the
composite. Together, they form the following class hierarchy:
IComponentGroupService
ICompositeGroupService extends
IComponentGroupService
IIndividualGroupService extends
ICompositeGroupService
IComponentGroupService. An IComponentGroupService
can get and set its name and, as a component in a composite, answer its
component group services. A component service can either contain
other component services or be a leaf service and serve groups.
While it may seem unlikely that services with groups of IPersons, like
LDAP or ERP-based services would be nested inside of other service
components, services with groups of ChannelDefinitions actually might,
particularly those representing groups of channels running on remote
portals.
public interface IComponentGroupService {
public Map getComponentServices();
public Name getServiceName();
public boolean isLeafService();
public void setServiceName(Name newServiceName);
}
The reference implementation is a true composite only at
service start-up, when each IComponentGroupService performs
a recursive retrieval of its components. Once the elements of
this composite have been retrieved, the composite service keeps its
components in a one-dimensional collection. Since it does not
contain nested group services, the reference composite group service
does not have a direct implementation of IComponentGroupService
but only implementations of its subtypes, ICompositeGroupService
and IIndividualGroupService .
ICompositeGroupService. An ICompositeGroupService
represents the entire composition. It is responsible for
delegating requests to the appropriate component service(s) and for
aggregating results. Requests come to the composite from
either the outside, (the service façade), or the inside, (a
component service). Some requests can be handled by a
single group service, for example, a request to find a specific group (findGroup() ,newGroup() ,
etc.) Other requests may span some or all of the component
services, for example, a request to find groups that contain a
particular group member (findContainingGroups() ) or a
request to find groups whose names contain a particular String (searchForGroups() ).
public interface ICompositeGroupService
extends IComponentGroupService
{
public Iterator findContainingGroups(IGroupMember
gm)
throws GroupsException;
public IEntityGroup findGroup(String key) throws
GroupsException;
public ILockableEntityGroup
findGroupWithLock(String key, String owner)
throws GroupsException;
public IEntity getEntity(String key, Class type)
throws GroupsException;
public IGroupMember getGroupMember(String key,
Class type)
throws GroupsException;
public IGroupMember getGroupMember(EntityIdentifier
underlyingEntityIdentifier) throws
GroupsException;
public IEntityGroup newGroup(Class type, Name
serviceName)
throws GroupsException;
public EntityIdentifier[] searchForEntities
(String query, int method, Class type)
throws GroupsException;
public EntityIdentifier[] searchForEntities
(String query, int method, Class type,
IEntityGroup ancestor)
throws GroupsException;
public EntityIdentifier[] searchForGroups
(String query, int method, Class
leaftype)
throws GroupsException;
public EntityIdentifier[] searchForGroups
(String query, int method, Class
leaftype, IEntityGroup ancestor)
throws GroupsException;
}
IIndividualGroupService. The third type,
IIndividualGroupService defines the methods that a specific or leaf
group service uses to read and write groups.
IIndividualGroupService inherits find() methods from
ICompositeGroupService, but whereas an ICompositeGroupService would
probably delegate these requests, an IIndividualGroupService would most
likely perform them itself. In addition, an
IIndividualGroupService must answer if one of its groups contains a
particular member (contains()), if the service can be updated (isEditable() ),
and more specifically, if it is possible to edit a particular group
(isEditable(IEntityGroup group) ). An attempt to
update a group that is not editable should throw a GroupsException.
public interface IIndividualGroupService
extends ICompositeGroupService
{
public boolean contains(IEntityGroup group, IGroupMember member)
throws GroupsException;
public void deleteGroup(IEntityGroup group)
throws GroupsException;
public IEntityGroup
findGroup(CompositeEntityIdentifier ent)
throws GroupsException;
public Iterator findMembers(IEntityGroup group)
throws GroupsException;
public boolean isEditable();
public boolean isEditable(IEntityGroup group)
throws GroupsException;
public IEntityGroup newGroup(Class type)
throws GroupsException;
public void updateGroup(IEntityGroup group)
throws GroupsException;
public void updateGroupMembers(IEntityGroup group)
throws GroupsException;
}
The reference implementation, ReferenceIndividualGroupService ,
delegates most requests to one of three sub-components, an IEntityGroupStore ,
an IEntityStore and an IEntitySearcher .
It also may use the portal's Entity Locking and Entity Caching
services.
Groups, Keys and
Service Names
Component Services and Their Names. Once it has
been assembled, the composite structure of the group service is
reflected in the names given to component services, which are
instances of javax.naming.Name with a node for each nested
service. Assuming a node separator of ".", a service named
"columbia" contained by a service named "remoteChannels" would be named
"remoteChannels.columbia". Since a component cannot be expected to
know in advance which components will contain it, the fully-qualified
service name of a given component may not be known until the composite
is fully assembled. In the reference implementation, the service
name is built up node by node as the composite is composed.
Group Keys. A
group's composite service key is the concatenation of its
fully-qualified service name and its key in the local service. The
nodes of the service name, and the final node of the name and the local
key, are separated by a node separator.
For example, a group with a local key of "English_Department" in a
service named "ldap" with a node separator of "%" would have a key of
"ldap%English_Department".
Node Separators.
The default node separator is the period, or ".", but it can be any String
not found within the nodes of a group key. For example, if local
group keys include "Latin.101.Section01" and "chefs@columbia.edu", valid
separators would include "$", "%", and "@@", but not "." or "@".
For instructions on changing the node separator see Configuring the Composite.
Groups and their Service
Names. The significance of the service name in a group key
is that it directs us to the specific service that can answer the
request for the group and it further qualifies the local key, which may
not be unique across different services. Thus, a client wishing
to find a group called "English_Department" in a component service
named "ldap" needs to ask the composite service for "ldap.English101" rather than "sis.English101" or simply
"English101". The ldap service may know the group by its local
key, but the client must know it by its fully-qualified, composite key.
Conversely, for a service to support foreign entries, where an
entry from one service participates in some way in another service, the
foreign entry must keep a reference to its home service so that it can
be retrieved and then navigate its
service of origin. As of version 2.1, IEntityGroup
inherits from org.jasig.portal.IBasicEntity , therefore a
group can already answer a key and a type, in the form of an org.jasig.portal.EntityIdentifier .
In the reference implementation, a group (an instance of EntityGroupImpl )
answers a subclass of EntityIdentifier , CompositeEntityIdentifier ,
whose key contains the fully-qualified service name in addition to the
local service key.
Entity Group Members and Their Keys. While a
group key is qualified by a service name, an entity key is not, and we
make the assumption that an entity group member (an IEntity )
is known across the composite service by a single key. All
component group services are obligated to know the entity by that
key. Thus, to get containing groups for IPerson “kweiner”, the
client would ask the service façade for an IGroupMember for
IPerson "kweiner", not for "ldap.kweiner" or "local.kweiner", and
then ask the group member for its containing groups. The composite
service would ask each of its components to get containing groups for
group member IPerson kweiner, and each component service would be
obligated to retrieve membership information for IPerson kweiner,
rather than for IPerson ldap.kweiner, IPerson local.kweiner, etc.
If the source of entities for a component group service stores entity
keys in a different format, e.g., "kw1" rather than "kweiner", the group
service must translate these keys from their native format to the
format understood by the other component services.
Assembling the
Composite
In the reference implementation, the composite service is an
instance of org.jasig.portal.groups.ReferenceCompositeGroupService
and is responsible for assembling the composite structure. Each
leaf component is an instance of ReferenceIndividualGroupService
and is customized with an IEntityGroupStore , an IEntityStore
and an IEntitySearcher . A factory class for each of
these types is specified in the configuration file.
The configuration file. The composite
configuration is stored in xml format in properties/groups/compositeGroupServices.xml .
The group service deployer edits this file to control the composition
of the composite group service. The root element of this document
is a service list whose service elements describe group
services that are top-level services, that is, not contained by another
service. In most installations, all services will be top-level
services. The configuration document is represented in Java
as a GroupServiceConfiguration , essentially a parser with
a Map of ComponentServiceDescriptors . Each ComponentServiceDescriptor
is itself a Map containing the elements and attributes of a single service
element. These elements are:
name
|
required
|
service_factory
|
required
|
entity_store_factory
|
required for reference implementation
|
group_store_factory
|
required for reference implementation
|
entity_searcher_factory
|
required for reference implementation
|
internally_managed
|
optional, defaults to false
|
caching_enabled
|
optional, defaults to false
|
Here is the service entry for the reference portal group service, which
is named "local":
<service>
<name>local</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.ReferenceEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.ReferenceEntityGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.ReferenceEntitySearcherFactory</entity_searcher_factory>
<internally_managed>true</internally_managed>
<caching_enabled>true</caching_enabled>
</service>
Creating the component services. On service
start-up, the composite service gets a GroupServiceConfiguration
and asks it for its service descriptors. The composite passes each
description to the appropriate service factory and gets back a new
service instance.
If the component is an individual or leaf service, the
factory creates an IIndividualGroupService , in the
reference implementation, a ReferenceIndividualGroupService .
The service instance uses the descriptor to customize itself, for
example, by getting its group store from the group store factory
designated in the descriptor. When the component has been
initialized, the composite service adds the new service to its
service Map.
If the service is not a leaf but a component service, the
composite service asks it for its
component services, which starts a recursive retrieval of leaf
services. The composite service completes the naming of each
leaf service by prepending the name of the top-level component to the
service name, and then adds each leaf component to its service Map.
At the end of the process, non-leaf components have been
eliminated, and the composite service may have multiple instances of the
same IIndividualGroupService implementation, each
customized by its own service descriptor.
Configuring the
Composite
The configuration described in compositeGroupServices.xml
is made available to group service classes via the utility class GroupServiceConfiguration .
This class exposes the servicelist attributes and service
elements via:
public Map getAttributes();
public List getServiceDescriptors()
uPortal 2.3 ships with the following configuration:
<?xml version="1.0"?>
<!-- $Revision$ -->
<!--
This list of component group services is processed by the composite, or
"root" service as it assembles itself. Each service element has 2
required elements: name and service_factory. The values of all
service elements are delivered to the service_factory.
-->
<servicelist defaultService="local"
compositeFactory="org.jasig.portal.groups.ReferenceCompositeGroupServiceFactory"
nodeSeparator=".">
<service>
<name>local</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.ReferenceEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.ReferenceEntityGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.ReferenceEntitySearcherFactory</entity_searcher_factory>
<internally_managed>true</internally_managed>
<caching_enabled>true</caching_enabled>
</service>
<!-- UNcomment to configure an LDAP group service
component. Be sure to edit LDAPGroupStoreConfig.xml with your
values. -->
<!--
<service>
<name>ldap</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.ldap.LDAPEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.ldap.LDAPGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.ldap.LDAPEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>false</caching_enabled>
</service>
-->
<!-- UNcomment to configure a file system group service
component. Change groupsRoot as appropriate. -->
<!--
<service groupsRoot="C:/groups">
<name>filesystem</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.filesystem.FileSystemGroupStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.filesystem.FileSystemGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.filesystem.FileSystemEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>false</caching_enabled>
</service>
-->
<!--
UNcomment to configure a Person Attributes group service
component. Configure your person attributes groups
in PAGSGroupStoreConfig.xml.
-->
<!--
<service>
<name>pags</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.pags.PersonAttributesEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.pags.PersonAttributesGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.pags.PersonAttributesEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>true</caching_enabled>
</service>
-->
</servicelist
Note that the servicelist element has 3 attributes, defaultService , compositeFactory
and nodeSeparator , and that 3 of the 4 service
entries are commented out.
Required servicelist Attributes. The attributes defaultService
and compositeFactory are both required.
<servicelist defaultService="local"
compositeFactory="org.jasig.portal.groups.ReferenceCompositeGroupServiceFactory"
nodeSeparator=".">
The defaultService is the service that responds
to requests for new group members when the request does not include a
service name, e.g., GroupService.newGroup(Class type) .
The entity factory in the default service supplies those IEntities
that are entry points into the composite group service (group members
not obtained from other group members.) One way to substitute an
alternate IEntity implementation for these entry points
would be to change the default service. (Another would be to
change the entity_store_factory element in the default
service.) The compositeFactory attribute designates
the class that creates the composite service instance. You would
change its value if you wanted to substitute your own composite service
implementation (and still use the configuration file.)
The nodeSeparator attribute is optional and
defaults to ".". For a description of its use and why you might
want to change it, see Group
Keys and Service Names. Changing the nodeSeparator changes the
format of the composite group key. If you have any pre-existing
data in the portal database when you change the nodeSeparator, you must
also change the format of any hard-coded composite group keys, replacing
the old nodeSeparator with the new. In the reference portal
database, composite group keys are found in the following tables:
UP_PERMISSION.PRINCIPAL_KEY
UP_GROUP_FRAGMENT.GROUP_KEY
UP_GROUP_MEMBERSHIP.MEMBER_SERVICE
They are also found in the following properties files:
portal.properties
(distinguished and root groups)
chanpub/categories.properties
chanpub/groups.properties
Individual channels may also store composite group keys, so
review your channels and their data stores.
Additional servicelist Attributes. The GroupServiceConfiguration
stores all servicelist attributes. If you wish to make additional
composite service attributes available to one or more of your component
services, you can add them to the configuration document and retrieve
them via:
String myAttribute = (String)
GroupServiceConfiguration.getAttributes().get("myAttribute");
The servicelist elements. The elements of the servicelist
describe top-level component services. The default
configuration contains a single service element named
"local". This is the default service whose group store is the
reference portal database. The service elements"ldap" and
"filesystem" are commented out. All 4 elements define leaf
services. The service named "local" has the following entry:
<service>
<name>local</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.ReferenceEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.ReferenceEntityGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.ReferenceEntitySearcherFactory</entity_searcher_factory>
<internally_managed>true</internally_managed>
<caching_enabled>true</caching_enabled>
</service>
Child Elements of the service element. Within the service
element, the name and service_factory child
elements are required. In addition, the reference
implementation of IIndividualGroupService requires all
child elements except internally_managed and
caching_enabled for a fully-functioning leaf service. The
elements are as follows:
service_factory designates the class name of the factory
that creates the service implementation. You only need to change
this value if you are substituting your own implementation for the
reference service implementation, ReferenceIndividualGroupService .
name of the service is significant if you need to use a
group from the service as a composite service entry point, since you
find such a group using a key that contains both the native key and
the service name, e.g.,
String nativeGroupKey = "100";
String serviceName = "local";
String nodeSep =
GroupServiceConfiguration.getConfiguration().getNodeSeparator();
String groupKey = serviceName + nodeSep +
nativeKey; // "local.100"
IGroupMember myGroup = GroupService.findGroup(groupKey);
Warning: The service name is part of the member
key in membership entries for member groups (groups that are members of
other groups.) If you change the name of a service, you must
change the keys of all membership entries for member groups
originating from that service.
entity_store_factory is the factory class name for the
entity store, the factory for IEntities . Since the
keys of IEntities do not contain service names, an
entity store can be shared by multiple group services. You would
change this value only if you were substituting your own
implementation of IEntityStore for the reference
implementation.
group_store_factory is the factory class name for the
group store, the adaptor that connects the group service with the native
source of groups information. Although entity stores can be
shared, each service will almost certainly have its own group
store. The "local" group store, RDBMEntityGroupStore ,
refers to tables in the reference portal database, contains sql
statements and retrieves group information via jdbc. The "ldap"
group store, org.jasig.portal.groups.ldap.LDAPGroupStore ,
refers to an LDAP database and submits LDAP queries over ldap:// .
The "filesystem" group store refers to filesystem files and directories
and gets its group information via java.io.
entity_searcher_factory is the factory class name for
the entity searcher implementation, a class that returns EntityIdentifiers
for potential group members. It is likely that each group service
will have its own entity searcher implementation (although some
implementations may be no-ops). The entity searcher for "local"
returns EntityIdentifiers for ChannelDefinitions
and IPersons from the reference portal database, while
the entity searcher for "ldap" returns EntityIdentifiers
for IPersons (but no ChannelDefs) from LDAP. The
entity searcher for "filesystem" is a no-op. Entity searchers have
proven necessary for group service clients like Groups Manager, which allow
interactive updates to the groups system. Users need to be able to
find the members they are looking for by searching by attributes like
last name, rather than by browsing through large numbers of
entries.
internally_managed contains a boolean value, either true
or false. If a service is internally-managed, it is
under the control of the portal and presumed to be capable of
writing as well as reading groups. In the reference
implementation, if internally_managed is true, the
service will attempt to satisfy a request to add, update or delete a
group. If it is not internally-managed, an attempt to update a
group will throw a GroupsException . An alternate
implementation of IIndividualGroupService could be more
selective and decide if updates are allowed on a group-by-group
basis.
caching_enabled has a boolean value, either true
or false, which controls whether the component service uses
the portal's entity caching service to cache groups. The value
for the "local" service is set to true to eliminate excess
database calls. It is set to false for the other services
because they do their own caching.
Additional services. Comment in one or more of the
other group service declarations if you want to supplement the local
groups with groups from other sources, (or create your own service):
<service>
<name>ldap</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.ldap.LDAPEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.ldap.LDAPGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.ldap.LDAPEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>false</caching_enabled>
</service>
<service groupsRoot=C:/groups>
<name>filesystem</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.filesystem.FileSystemGroupStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.filesystem.FileSystemGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.filesystem.FileSystemEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>false</caching_enabled>
</service>
<service>
<name>pags</name>
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
<entity_store_factory>org.jasig.portal.groups.pags.PersonAttributesEntityStoreFactory</entity_store_factory>
<group_store_factory>org.jasig.portal.groups.pags.PersonAttributesGroupStoreFactory</group_store_factory>
<entity_searcher_factory>org.jasig.portal.groups.pags.PersonAttributesEntitySearcherFactory</entity_searcher_factory>
<internally_managed>false</internally_managed>
<caching_enabled>true</caching_enabled>
</service>
Note that the value of service_factory is the same for
all of the service elements. All of the services are instances of ReferenceIndividualGroupService ,
customized by their respective service configurations. In the
case of the "ldap", "pags" and "filesystem" services, the entries for entity_store_factory ,
group_store_factory and entity_searcher_factory all
designate different factory classes but return a single group store
instance that implements IEntityStore , IEntityGroupStore
and IEntitySearcher . These services have internally_managed
set to false since updates to them occur outside the
transactional control of the portal and, in any event, their stores do
not support updates.
Caching of Group
Members
Caching of Groups.
When caching is set on for a
component service (caching_enabled=true) , the composite
group service uses the portal's Entity Caching Service to cache group
members that come from the component service. On update, a cached
group is either removed from the cache or replaced, and cached copies of
the group on other servers are invalidated. This saves the
component service from having to check if a group is up-to-date each
time it is requested and is appropriate for a group service that manages
group updates and is therefore in a position to know when they
happen.
On the other hand, an externally-managed group service (internally_managed=false )
might not benefit at all from group caching by the caching
service. If the group source is dynamic, the service has to check
for updates anyway, and if the source is static, the service doesn't
have to check at all. Either way, a simple cache maintained by the
service itself is probably more efficient (caching_enabled=false ).
Membership Changes for Cached
Entities. Unless a service name is specified, the
composite group service methods getGroupMember() and getEntity()
delegate the creation of IEntities to the default component
group service, typically the local
group service. Since this service is always internally-managed, IEntities
are by default cached in the caching service. This is a big
optimization, but it can lead to a couple of apparent anomolies.
If an IEntity is added or removed from an internally-managed group, the IEntity's
group memberships are updated in real time in the same portal JVM, and
within a configurable interval in caches on linked portal JVMs.
But if the IEntity is added or removed from an externally-managed group, the group
service will not detect the change directly, and cached copies of the IEntity
will not be updated. If the group is then re-retrieved, it will
have the update, but the cached entities will not. In the case of IEntities
for IPersons (users), we remove the cache entry when the
user's portal session is destroyed, but this still requires the user to
log off and log on again to see the effect of a concurrent membership
change in an external group source.
Key Caches. Groups
cache the keys of their members so they do not have to keep
re-retrieving membership information. These caches are initialized
when a group is first asked for its members. If a group has a
large number of members, this process is expensive, so it is not
performed unless getMembers() is actually called. For
example, if contains() is called on a group that has not
yet been initialized, the group will delegate this method to its store
to spare it the expense of initialization just to answer if it contains
a single group member.
Alternate Group Stores
The composite design anticipates that a group system will have
multiple sources of groups. The group information that ships with
the basic uPortal distribution comes from the local group service, which
uses RDBMEntityGroupStore as its store. As of version
2.3, there are 3 alternate stores that come with uPortal, described at
the links below. If none of them meets your needs, consider
writing a custom store.
Next Steps
The process of deploying a composite group service involves
(at least) the following steps:
- analyze why you need group information
- identify the necessary sources of group information
- find or create adaptors for these sources
- configure the composite service
Most portals will at least use groups to manage authorization,
so this is a common starting point. Many institutions will rely on
an LDAP service as the primary source of group information and
supplement it with information from the portal database. Others
may require additional information from human resources, student
information or other systems. uPortal currently ships with 4
adaptors, 1 for the reference portal database (RDBMEntityGroupStore )
and 3 alternate stores (see above.) If you only intend to use
these 4 sources, you do not have to write a custom group store. If
you do need to draw groups from another source, you will have to
implement the IEntityGroupStore and IEntitySearcher
interfaces. You should not have to write a custom group serivce
(an IIndividualGroupService ) unless you need to change the
transactional rules of the service. Of course, you can always
re-implement or sublcass the reference implementations for reasons of
efficiency, correctness or to add new functions. Please
contribute your code back to the project so that the entire uPortal
community can benefit from your improvements.
The final step is to represent your composite group service in
the configuration file. Describe each top-level service in a service
element and designate the default service. Unless you have a
specific reason for changing it, keep the initial default value of
"local". If a custom service supports updates, set internally_managed
to true. Note that you must implement the update methods
for such a service in a custom group store class. Of the 4 group
store implementations that ship with uPortal, only the local store
supports updates. Caching of groups is a must in a production
system. Either use the portal Entity Caching Service or implement
your own caching mechanism.
Please post your questions, comments, suggestions and ideas to
the JA-SIG Portal Discussion List.
Good luck!
last revised: 03/31/2004, d.e.
|