...
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:
Code Block |
---|
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.
...
Code Block |
---|
public interfaceIComponentGroupService{
public Map getComponentServices();
public Name getServiceName();
public boolean isLeafService();
public void setServiceName(Name newServiceName);
}
{code}
|
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()).
...
Code Block |
---|
public interfaceICompositeGroupServiceextends 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;
}
{code}
_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 |
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.
...
Code Block |
---|
public interfaceIIndividualGroupServiceextends 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;
}
{code}
|
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 a String containing a period, or ".",
...
but
...
it
...
can
...
be
...
any
...
String
...
not
...
found
...
within
...
the
...
nodes
...
of
...
a
...
group
...
key.
...
However,
...
group
...
keys
...
can
...
find
...
their
...
way
...
into
...
portal
...
content
...
and
...
urls
...
and
...
are
...
subject
...
to
...
xsl
...
transformations.
...
Therefore,
...
the
...
separator
...
should
...
not
...
contain
...
a
...
character
...
like
...
'%'
...
that
...
has
...
a
...
special
...
meaning
...
and
...
must
...
be
...
escaped
...
to
...
preserve
...
its
...
literal
...
value.
...
If
...
local
...
group
...
keys
...
include
...
"Latin.101.Section01"
...
and
...
"chefs@columbia.edu",
...
valid
...
separators
...
would
...
include
...
"$",
...
and
...
"_x",
...
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":
Code Block |
---|
<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>
{code}
_ |
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:
Code Block |
---|
<?xml version="1.0"?>
<!-- $Revision: 1.15 $ -->
<!--
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
{code}
----
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.
{code} |
...
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.
Code Block |
---|
<servicelist defaultService="local"
compositeFactory="org.jasig.portal.groups.ReferenceCompositeGroupServiceFactory"
nodeSeparator=".">
{code}
|
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:
Code Block |
---|
String myAttribute = (String)
GroupServiceConfiguration.getAttributes().get("myAttribute");
{code}
_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:
{code} |
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:
Code Block |
---|
<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>
{code}
_ |
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
...
andcaching_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.,
Code Block |
---|
String nativeGroupKey = "100";
String serviceName = "local";
String nodeSep = GroupServiceConfiguration.getConfiguration().getNodeSeparator();
String groupKey = serviceName + nodeSep + nativeKey; // "local.100"
IGroupMember myGroup = GroupService.findGroup(groupKey);
{code}
_ |
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):
Code Block |
---|
<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>
{code}
|
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.
- The LDAP Group Store converts LDAP attributes into group memberships.
- The Person Attributes Group Store makes IPerson attributes retrieved by the PersonDirectory class appear to be group memberships.
- The Filesystem Group Store lets you create a group system from lists of user IDs in file system files.
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!