directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From smckin...@apache.org
Subject [37/75] [abbrv] [partial] directory-fortress-core git commit: FC-109 - rename rbac package to impl
Date Tue, 09 Jun 2015 03:15:43 GMT
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java b/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
new file mode 100755
index 0000000..288d499
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
@@ -0,0 +1,425 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReadWriteLock;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.fortress.core.model.Graphable;
+import org.apache.directory.fortress.core.model.Hier;
+import org.apache.directory.fortress.core.model.ParentUtil;
+import org.apache.directory.fortress.core.model.Relationship;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.UserRole;
+import org.apache.directory.fortress.core.util.ObjUtil;
+import org.jgrapht.graph.SimpleDirectedGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ValidationException;
+import org.apache.directory.fortress.core.util.cache.Cache;
+import org.apache.directory.fortress.core.util.cache.CacheMgr;
+
+
+/**
+ * This utility wraps {@link org.apache.directory.fortress.core.impl.HierUtil} methods to provide hierarchical functionality for the {@link org.apache.directory.fortress.core.model.Role} data set.
+ * The {@code cn=Hierarchies, ou=Roles} data is stored within a cache, {@link #roleCache}, contained within this class.  The parent-child edges are contained in LDAP,
+ * in {@code ftParents} attribute.  The ldap data is retrieved {@link org.apache.directory.fortress.core.impl.RoleP#getAllDescendants(String)} and loaded into {@code org.jgrapht.graph.SimpleDirectedGraph}.
+ * The graph...
+ * <ol>
+ * <li>is stored as singleton in this class with vertices of {@code String}, and edges, as {@link org.apache.directory.fortress.core.model.Relationship}s</li>
+ * <li>utilizes open source library, see <a href="http://www.jgrapht.org/">JGraphT</a>.</li>
+ * <li>contains a general hierarchical data structure i.e. allows multiple inheritance with parents.</li>
+ * <li>is a simple directed graph thus does not allow cycles.</li>
+ * </ol>
+ * After update is performed to ldap, the singleton is refreshed with latest info.
+ * <p/>
+ * Static methods on this class are intended for use by other Fortress classes, i.e. {@link org.apache.directory.fortress.core.impl.UserDAO} and {@link org.apache.directory.fortress.core.impl.PermDAO}
+ * and cannot be directly invoked by outside programs.
+ * <p/>
+ * This class contains singleton that can be updated but is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public final class RoleUtil implements ParentUtil
+{
+    private static final Cache roleCache;
+    private static final RoleP roleP = new RoleP();
+    private static final String CLS_NM = RoleUtil.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+    /**
+     * Initialize the Role hierarchies.  This will read the {@link org.apache.directory.fortress.core.model.Hier} data set from ldap and load into
+     * the JGraphT simple digraph that referenced statically within this class.
+     */
+    static
+    {
+        CacheMgr cacheMgr = CacheMgr.getInstance();
+        roleCache = cacheMgr.getCache( "fortress.roles" );
+    }
+
+    /**
+     * Private constructor
+     *
+     */
+    private void RoleUtil()
+    {
+    }
+
+    /**
+     * Used to determine if one {@link org.apache.directory.fortress.core.model.Role} is the parent of another.  This method
+     * will call recursive routine {@link #getAscendants(String, String)} to walk the {@code org.jgrapht.graph.SimpleDirectedGraph} data structure
+     * returning flag indicating if parent-child relationship is valid.
+     *
+     * @param child  maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param parent maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRels' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return boolean result, 'true' indicates parent/child relationship exists.
+     */
+    public static boolean isParent( String child, String parent, String contextId )
+    {
+        boolean result = false;
+        Set<String> parents = getAscendants( child, contextId );
+        if ( parents != null && parents.size() > 0 )
+        {
+            result = parents.contains( parent.toUpperCase() );
+        }
+        return result;
+    }
+
+
+    /**
+     * Recursively traverse the {@link org.apache.directory.fortress.core.model.Role} graph and return all of the descendants of a given node {@link org.apache.directory.fortress.core.model.Role#name}.
+     *
+     * @param roleName {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of Role names are descendants {@link org.apache.directory.fortress.core.model.Role}s of given parent.
+     */
+    public static Set<String> getDescendants( String roleName, String contextId )
+    {
+        return HierUtil.getDescendants( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Traverse the {@link org.apache.directory.fortress.core.model.Role} graph and return all children (direct descendants) of a given parent node {@link org.apache.directory.fortress.core.model.Role#name}.
+     *
+     * @param roleName {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of Role names are children {@link org.apache.directory.fortress.core.model.Role}s of given parent.
+     */
+    public static Set<String> getChildren( String roleName, String contextId )
+    {
+        return HierUtil.getChildren( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical role graph and return all of the ascendants of a given role.
+     *
+     * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of Role names that are ascendants of given child.
+     */
+    public static Set<String> getAscendants( String roleName, String contextId )
+    {
+        return HierUtil.getAscendants( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Traverse the hierarchical role graph and return all of the parents (direct ascendants) of a given role.
+     *
+     * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of Role names that are parents of given child.
+     */
+    static Set<String> getParents( String roleName, String contextId )
+    {
+        return HierUtil.getParents( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Traverse the hierarchical role graph and return all of the parents (direct ascendants) of a given role.
+     *
+     * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of Role names that are parents of given child.
+     */
+    public Set<String> getParentsCB( String roleName, String contextId )
+    {
+        return HierUtil.getParents( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Determine the number of children (direct descendants) a given parent role has.
+     *
+     * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return int value contains the number of children of a given parent nRole.
+     */
+    static int numChildren( String roleName, String contextId )
+    {
+        return HierUtil.numChildren( roleName.toUpperCase(), getGraph( contextId ) );
+    }
+
+
+    /**
+     * Return Set of RBAC {@link org.apache.directory.fortress.core.model.Role#name}s ascendants.  Used by {@link org.apache.directory.fortress.core.impl.PermDAO#checkPermission}
+     * for computing authorized {@link org.apache.directory.fortress.core.model.UserRole#name}s.
+     *
+     * @param uRoles contains list of Roles activated within a {@link org.apache.directory.fortress.core.model.User}'s {@link org.apache.directory.fortress.core.model.Session}.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return contains Set of all authorized RBAC Roles for a given User.
+     */
+    public static Set<String> getInheritedRoles( List<UserRole> uRoles, String contextId )
+    {
+        // create Set with case insensitive comparator:
+        Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        if ( ObjUtil.isNotNullOrEmpty( uRoles ) )
+        {
+            for ( UserRole uRole : uRoles )
+            {
+                String rleName = uRole.getName();
+                iRoles.add( rleName );
+                Set<String> parents = HierUtil.getAscendants( rleName, getGraph( contextId ) );
+                if ( ObjUtil.isNotNullOrEmpty( parents ) )
+                {
+                    iRoles.addAll( parents );
+                }
+            }
+        }
+        return iRoles;
+    }
+
+
+    /**
+     *
+     * @param roles
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return set of ascendant roles associated with this entry.
+     */
+    static Set<String> getAscendantRoles( List<String> roles, String contextId )
+    {
+        // create Set with case insensitive comparator:
+        Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        if ( ObjUtil.isNotNullOrEmpty( roles ) )
+        {
+            for ( String role : roles )
+            {
+                iRoles.add( role );
+                Set<String> parents = HierUtil.getAscendants( role, getGraph( contextId ) );
+                if ( ObjUtil.isNotNullOrEmpty( parents ) )
+                {
+                    iRoles.addAll( parents );
+                }
+            }
+        }
+        return iRoles;
+    }
+
+
+    /**
+     *
+     * @param roles
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return set of descendant roles associated with this entry.
+     */
+    static Set<String> getDescendantRoles( Set<String> roles, String contextId )
+    {
+        // create Set with case insensitive comparator:
+        Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        if ( ObjUtil.isNotNullOrEmpty( roles ) )
+        {
+            for ( String role : roles )
+            {
+                iRoles.add( role );
+                Set<String> children = HierUtil.getDescendants( role, getGraph( contextId ) );
+                if ( ObjUtil.isNotNullOrEmpty( children ) )
+                {
+                    iRoles.addAll( children );
+                }
+            }
+        }
+        return iRoles;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and return all of the ascendants of a given child node.
+     *
+     * @param childName   maps to vertex to determine parentage.
+     * @param parentName  points to top most ascendant where traversal must stop.
+     * @param isInclusive if set to true will include the parentName in the result set.  False will not return specified parentName.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of names that are parents of given child.
+     */
+    static Set<String> getAscendants( String childName, String parentName, boolean isInclusive, String contextId )
+    {
+        return HierUtil.getAscendants( childName, parentName, isInclusive, getGraph( contextId ) );
+    }
+
+
+    /**
+     * This api is used by {@link AdminMgrImpl} to determine parentage for Hierarchical RBAC processing.
+     * It calls {@link HierUtil#validateRelationship(org.jgrapht.graph.SimpleDirectedGraph, String, String, boolean)} to evaluate three adminRole relationship expressions:
+     * <ol>
+     * <li>If child equals parent</li>
+     * <li>If mustExist true and parent-child relationship exists</li>
+     * <li>If mustExist false and parent-child relationship does not exist</li>
+     * </ol>
+     * Method will throw {@link org.apache.directory.fortress.core.ValidationException} if rule check fails meaning caller failed validation
+     * attempt to add/remove hierarchical relationship failed.
+     *
+     * @param childRole  contains {@link org.apache.directory.fortress.core.model.Role#name} of child.
+     * @param parentRole contains {@link org.apache.directory.fortress.core.model.Role#name} of parent.
+     * @param mustExist  boolean is used to specify if relationship must be true.
+     * @throws org.apache.directory.fortress.core.ValidationException
+     *          in the event it fails one of the 3 checks.
+     */
+    static void validateRelationship( Role childRole, Role parentRole, boolean mustExist )
+        throws ValidationException
+    {
+        HierUtil.validateRelationship( getGraph( childRole.getContextId() ), childRole.getName(), parentRole.getName(),
+            mustExist );
+    }
+
+
+    /**
+     * This api allows synchronized access to allow updates to hierarchical relationships.
+     * Method will update the hierarchical data set and reload the JGraphT simple digraph with latest.
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @param relationship contains parent-child relationship targeted for addition.
+     * @param op   used to pass the ldap op {@link org.apache.directory.fortress.core.model.Hier.Op#ADD}, {@link org.apache.directory.fortress.core.model.Hier.Op#MOD}, {@link org.apache.directory.fortress.core.model.Hier.Op#REM}
+     * @throws org.apache.directory.fortress.core.SecurityException in the event of a system error.
+     */
+    static void updateHier( String contextId, Relationship relationship, Hier.Op op ) throws SecurityException
+    {
+        HierUtil.updateHier( getGraph( contextId ), relationship, op );
+    }
+
+
+    /**
+     * Read this ldap record,{@code cn=Hierarchies, ou=OS-P} into this entity, {@link Hier}, before loading into this collection class,{@code org.jgrapht.graph.SimpleDirectedGraph}
+     * using 3rd party lib, <a href="http://www.jgrapht.org/">JGraphT</a>.
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return handle to simple digraph containing role hierarchies.
+     */
+    private static SimpleDirectedGraph<String, Relationship> loadGraph( String contextId )
+    {
+        Hier inHier = new Hier( Hier.Type.ROLE );
+        inHier.setContextId( contextId );
+        LOG.info( "loadGraph initializing ROLE context [{}]", inHier.getContextId() );
+        List<Graphable> descendants = null;
+
+        try
+        {
+            descendants = roleP.getAllDescendants( inHier.getContextId() );
+        }
+        catch ( SecurityException se )
+        {
+            LOG.info( "loadGraph caught SecurityException={}", se );
+        }
+
+        Hier hier = HierUtil.loadHier( contextId, descendants );
+        SimpleDirectedGraph<String, Relationship> graph;
+
+        graph = HierUtil.buildGraph( hier );
+        roleCache.put( getKey( contextId ), graph );
+
+        return graph;
+    }
+
+
+    /**
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return key to this tenant's cache entry.
+     */
+    private static String getKey( String contextId )
+    {
+        String key = HierUtil.Type.ROLE.toString();
+
+        if ( StringUtils.isNotEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) )
+        {
+            key += ":" + contextId;
+        }
+
+        return key;
+    }
+
+
+    /**
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return handle to simple digraph containing role hierarchies.
+     */
+    private static SimpleDirectedGraph<String, Relationship> getGraph( String contextId )
+    {
+        ReadWriteLock hierLock = HierUtil.getLock( contextId, HierUtil.Type.ROLE );
+        String key = getKey( contextId );
+
+        try
+        {
+            hierLock.readLock().lock();
+            SimpleDirectedGraph<String, Relationship> graph = ( SimpleDirectedGraph<String, Relationship> ) roleCache
+                .get( key );
+
+            if ( graph == null )
+            {
+                try
+                {
+                    hierLock.readLock().unlock();
+                    hierLock.writeLock().lock();
+
+                    // TODO: determine why this (code that was commented out) creates a deadlock:
+                    //graph = ( SimpleDirectedGraph<String, Relationship> ) roleCache.get( key );
+
+                    //if ( graph == null )
+                    //{
+                    graph = loadGraph( contextId );
+                    //}
+
+                    hierLock.readLock().lock();
+                }
+                finally
+                {
+                    hierLock.writeLock().unlock();
+                }
+            }
+
+            return graph;
+        }
+        finally
+        {
+            hierLock.readLock().unlock();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java b/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
new file mode 100755
index 0000000..da7be48
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
@@ -0,0 +1,548 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.ReviewMgrFactory;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ReviewMgr;
+import org.apache.directory.fortress.core.util.Config;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.SDSet;
+import org.apache.directory.fortress.core.model.Session;
+import org.apache.directory.fortress.core.model.User;
+import org.apache.directory.fortress.core.model.UserRole;
+import org.apache.directory.fortress.core.util.ObjUtil;
+import org.apache.directory.fortress.core.util.cache.Cache;
+import org.apache.directory.fortress.core.util.cache.CacheMgr;
+import org.apache.directory.fortress.core.util.cache.DsdCacheEntry;
+import org.apache.directory.fortress.core.util.time.Constraint;
+
+import net.sf.ehcache.search.Attribute;
+import net.sf.ehcache.search.Query;
+import net.sf.ehcache.search.Result;
+import net.sf.ehcache.search.Results;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This utilty provides functionality necessary for SSD and DSD processing and cannot be called by components outside fortress.
+ * This class also contains utility functions for maintaining the SSD and DSD cache.
+ * <p/>
+ * This class is thread safe.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @created September 3, 2010
+ */
+final class SDUtil
+{
+    private static final Cache m_dsdCache;
+    private static final String FORTRESS_DSDS = "fortress.dsd";
+    private static final Cache m_ssdCache;
+    private static final String FORTRESS_SSDS = "fortress.ssd";
+    private static final SdP sp = new SdP();
+    private static final String IS_DSD_CACHE_DISABLED_PARM = "enable.dsd.cache";
+    private static final String DSD_NAME = "name";
+    private static final String EMPTY_ELEMENT = "empty";
+    private static final String CONTEXT_ID = "contextId";
+
+    static
+    {
+        // Get a reference to the CacheManager Singleton object:
+        CacheMgr cacheMgr = CacheMgr.getInstance();
+        // This cache contains a wrapper entry for DSD and is searchable by both DSD and Role name:
+        m_dsdCache = cacheMgr.getCache(FORTRESS_DSDS);
+        // This cache is not searchable and contains Lists of SSD objects by Role:
+        m_ssdCache = cacheMgr.getCache(FORTRESS_SSDS);
+    }
+
+    /**
+     * Private constructor
+     *
+     */
+    private SDUtil()
+    {
+    }
+
+    /**
+     * This method is called by AdminMgr.assignUser and is used to validate Static Separation of Duty
+     * constraints when assigning a role to user.
+     *
+     * @param uRole
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *
+     */
+    static void validateSSD(UserRole uRole)
+        throws SecurityException
+    {
+        validateSSD(new User(uRole.getUserId()), new Role(uRole.getName()));
+    }
+
+    /**
+     * This method is called by AdminMgr.assignUser and is used to validate Static Separation of Duty
+     * constraints when assigning a role to user.
+     *
+     * @param user
+     * @param role
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *
+     */
+    static void validateSSD(User user, Role role)
+        throws SecurityException
+    {
+        int matchCount;
+        // get all authorized roles for user
+        ReviewMgr rMgr = ReviewMgrFactory.createInstance(user.getContextId());
+        Set<String> rls = rMgr.authorizedRoles(user);
+        // Need to proceed?
+        if (!ObjUtil.isNotNullOrEmpty(rls))
+        {
+            return;
+        }
+
+        // get all SSD sets that contain the new role
+        List<SDSet> ssdSets = getSsdCache(role.getName(), user.getContextId());
+        for (SDSet ssd : ssdSets)
+        {
+            matchCount = 0;
+            Set<String> map = ssd.getMembers();
+            // iterate over every authorized role for user:
+            for (String authRole : rls)
+            {
+                // is there a match found between authorized role and SSD set's members?
+                if (map.contains(authRole))
+                {
+                    matchCount++;
+                    // does the match count exceed the cardinality allowed for this particular SSD set?
+                    if (matchCount >= ssd.getCardinality() - 1)
+                    {
+                        String error = "validateSSD new role [" + role.getName() + "] validates SSD Set Name:" + ssd.getName() + " Cardinality:" + ssd.getCardinality();
+                        throw new SecurityException(GlobalErrIds.SSD_VALIDATION_FAILED, error);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is called by AccessMgr.addActiveRole and is used to validate Dynamic Separation of Duty
+     * constraints when activating a role one at a time.  For activation of multiple roles simultaneously use
+     * the DSD.validate API which is used during createSession sequence.
+     *
+     * @param session
+     * @param role
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *
+     */
+    static void validateDSD(Session session, Constraint role)
+        throws SecurityException
+    {
+        // get all activated roles from user's session:
+        List<UserRole> rls = session.getRoles();
+        if (!ObjUtil.isNotNullOrEmpty(rls))
+        {
+            // An empty list of roles was passed in the session variable.
+            // No need to continue.
+            return;
+        }
+
+        // get all DSD sets that contain the target role
+        Set<SDSet> dsdSets = getDsdCache(role.getName(), session.getContextId());
+        for (SDSet dsd : dsdSets)
+        {
+            // Keeps the number of matched roles to a particular DSD set.
+            int matchCount = 0;
+
+            // Contains the list of roles assigned to a particular DSD set.
+            Set<String> map = dsd.getMembers();
+
+            // iterate over every role active in session for match wth DSD members:
+            for (UserRole actRole : rls)
+            {
+                // is there a match found between active role in session and DSD set members?
+                if (map.contains(actRole.getName()))
+                {
+                    // Yes, we found a match, increment the count.
+                    matchCount++;
+
+                    // Does the match count exceed the cardinality allowed for this particular DSD set?
+                    if (matchCount >= dsd.getCardinality() - 1)
+                    {
+                        // Yes, the target role violates DSD cardinality rule.
+                        String error = "validateDSD failed for role [" + role.getName() + "] DSD Set Name:" + dsd.getName() + " Cardinality:" + dsd.getCardinality();
+                        throw new SecurityException(GlobalErrIds.DSD_VALIDATION_FAILED, error);
+                    }
+                }
+                else // Check the parents of activated role for DSD match:
+                {
+                    // Now pull the activated role's list of parents.
+                    Set<String> parentSet = RoleUtil.getAscendants(actRole.getName(), session.getContextId());
+
+                    // Iterate over the list of parent roles:
+                    for (String parentRole : parentSet)
+                    {
+                        if (map.contains(parentRole)) // is there match between parent and DSD member?
+                        {
+                            matchCount++;
+                            if (matchCount >= dsd.getCardinality() - 1) // Does the counter exceed max per cardinality on this DSD set?
+                            {
+                                String error = "validateDSD failed for role [" + role.getName() + "] parent role [" + parentRole + "] DSD Set Name:" + dsd.getName() + " Cardinality:" + dsd.getCardinality();
+                                throw new SecurityException(GlobalErrIds.DSD_VALIDATION_FAILED, error);
+                            }
+                            // Breaking out of the loop here means the DSD algorithm will only match one
+                            // role per parent of active role candidate.
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Given DSD entry name, clear its corresponding object values from the cache.
+     *
+     * @param name contains the name of object to be cleared.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.     *
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    static void clearDsdCacheEntry(String name, String contextId)
+    {
+        Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+        Attribute<String> dsdName = m_dsdCache.getSearchAttribute(DSD_NAME);
+        Query query = m_dsdCache.createQuery();
+        query.includeKeys();
+        query.includeValues();
+        query.addCriteria(dsdName.eq(name).and(context.eq(contextId)));
+        Results results = query.execute();
+        for (Result result : results.all())
+        {
+            m_dsdCache.clear(result.getKey());
+        }
+    }
+
+    /**
+     * Given a role name, return the set of DSD's that have a matching member.
+     *
+     * @param name contains name of authorized Role used to search the cache.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return un-ordered set of matching DSD's.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    private static Set<SDSet> getDsdCache(String name, String contextId)
+        throws SecurityException
+    {
+        contextId = getContextId(contextId);
+        Set<SDSet> finalSet = new HashSet<>();
+        Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+        Attribute<String> member = m_dsdCache.getSearchAttribute(SchemaConstants.MEMBER_AT);
+        Query query = m_dsdCache.createQuery();
+        query.includeKeys();
+        query.includeValues();
+        query.addCriteria(member.eq(name).and(context.eq(contextId)));
+        Results results = query.execute();
+        boolean empty = false;
+        for (Result result : results.all())
+        {
+            DsdCacheEntry entry = (DsdCacheEntry) result.getValue();
+            if (!entry.isEmpty())
+            {
+                finalSet.add(entry.getSdSet());
+                finalSet = putDsdCache(name, contextId);
+            }
+            else
+            {
+                empty = true;
+            }
+            finalSet.add(entry.getSdSet());
+        }
+        // If nothing was found in the cache, determine if it needs to be seeded:
+        if (finalSet.size() == 0 && !empty)
+        {
+            finalSet = putDsdCache(name, contextId);
+        }
+        return finalSet;
+    }
+
+    /**
+     * Given a Set of authorized Roles, return the set of DSD's that have matching members.
+     *
+     * @param authorizedRoleSet contains an un-order Set of authorized Roles.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return un-ordered set of matching DSD's.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    static Set<SDSet> getDsdCache(Set<String> authorizedRoleSet, String contextId)
+        throws SecurityException
+    {
+        contextId = getContextId(contextId);
+        Set<SDSet> dsdRetSets = new HashSet<>();
+        // Need to proceed?
+        if (!ObjUtil.isNotNullOrEmpty(authorizedRoleSet))
+        {
+            return dsdRetSets;
+        }
+        // Was the DSD Cache switched off?
+        boolean isCacheDisabled = Config.getBoolean(IS_DSD_CACHE_DISABLED_PARM, false);
+        // If so, get DSD's from LDAP:
+        if (isCacheDisabled)
+        {
+            SDSet sdSet = new SDSet();
+            sdSet.setType(SDSet.SDType.DYNAMIC);
+            sdSet.setContextId(contextId);
+            dsdRetSets = sp.search(authorizedRoleSet, sdSet);
+        }
+        // Search the DSD cache for matching Role members:
+        else
+        {
+            // Search on roleName attribute which maps to 'member' attr on the cache record:
+            Attribute<String> member = m_dsdCache.getSearchAttribute(SchemaConstants.MEMBER_AT);
+            Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+            Query query = m_dsdCache.createQuery();
+            query.includeKeys();
+            query.includeValues();
+            // Add the passed in authorized Role names to this cache query:
+            Set<String> roles = new HashSet<>(authorizedRoleSet);
+            query.addCriteria(member.in(roles).and(context.eq(contextId)));
+            // Return all DSD cache entries that match roleName to the 'member' attribute in cache entry:
+            Results results = query.execute();
+            for (Result result : results.all())
+            {
+                DsdCacheEntry entry = (DsdCacheEntry) result.getValue();
+                // Do not add dummy DSD sets to the final list:
+                if (!entry.isEmpty())
+                {
+                    dsdRetSets.add(entry.getSdSet());
+                }
+                // Remove role member from authorizedRoleSet to preclude from upcoming DSD search:
+                authorizedRoleSet.remove(entry.getMember());
+            }
+            // Authorized roles remaining in this set correspond to missed cache hits from above:
+            if (authorizedRoleSet.size() > 0)
+            {
+                dsdRetSets = putDsdCache(authorizedRoleSet, contextId);
+            }
+        }
+        return dsdRetSets;
+    }
+
+    /**
+     * Get the matching DSD's from directory and add to the cache (if found).  If matching DSD not found,
+     * add dummy entry to cache to prevent repeated searches.
+     *
+     * @param authorizedRoleSet contains set of Roles used to search directory for matching DSD's.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return List of DSD's who have matching Role members.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    private static Set<SDSet> putDsdCache(Set<String> authorizedRoleSet, String contextId)
+        throws SecurityException
+    {
+        contextId = getContextId(contextId);
+        Set<SDSet> dsdSets = new HashSet<>();
+        // Search the DSD's iteratively to seed the DSD cache by Role name:
+        for (String roleName : authorizedRoleSet)
+        {
+            Role role = new Role(roleName);
+            role.setContextId(contextId);
+            List<SDSet> dsdList = sp.search(role, SDSet.SDType.DYNAMIC);
+            if (ObjUtil.isNotNullOrEmpty(dsdList))
+            {
+                for (SDSet dsd : dsdList)
+                {
+                    dsd.setContextId(contextId);
+                    Set<String> members = dsd.getMembers();
+                    if (members != null)
+                    {
+                        // Seed the cache with DSD objects mapped to role name:
+                        for (String member : members)
+                        {
+                            String key = buildKey(dsd.getName(), member);
+                            DsdCacheEntry entry = new DsdCacheEntry(member, dsd, false);
+                            entry.setName(dsd.getName());
+                            m_dsdCache.put(getKey(key, contextId), entry);
+                        }
+                    }
+                }
+                // Maintain the set of DSD's to be returned to the caller:
+                dsdSets.addAll(dsdList);
+            }
+            else
+            {
+                // Seed the cache with dummy entry for a Role that is not referenced by DSD:
+                String key = buildKey(EMPTY_ELEMENT, roleName);
+                SDSet sdSet = new SDSet();
+                sdSet.setType(SDSet.SDType.DYNAMIC);
+                sdSet.setName(key);
+                sdSet.setMember(roleName);
+                sdSet.setContextId(contextId);
+                DsdCacheEntry entry = new DsdCacheEntry(roleName, sdSet, true);
+                entry.setName(key);
+                m_dsdCache.put(getKey(sdSet.getName(), contextId), entry);
+            }
+        }
+        return dsdSets;
+    }
+
+    /**
+     * Get the matching DSD's from directory and add to the cache (if found).  If matching DSD not found,
+     * add dummy entry to cache to prevent repeated searches.
+     *
+     * @param roleName of Role is used to search directory for matching DSD's.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return Set of DSD's who have matching Role member.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    private static Set<SDSet> putDsdCache(String roleName, String contextId)
+        throws SecurityException
+    {
+        contextId = getContextId(contextId);
+        Role role = new Role(roleName);
+        role.setContextId(contextId);
+        List<SDSet> dsdList = sp.search(role, SDSet.SDType.DYNAMIC);
+        Set<SDSet> finalSet = new HashSet<>(dsdList);
+        if ( ObjUtil.isNotNullOrEmpty( dsdList ))
+        {
+            for (SDSet dsd : dsdList)
+            {
+                dsd.setContextId(contextId);
+                Set<String> members = dsd.getMembers();
+                if (members != null)
+                {
+                    // Seed the cache with DSD objects mapped to role name:
+                    for (String member : members)
+                    {
+                        String key = buildKey(dsd.getName(), member);
+                        DsdCacheEntry entry = new DsdCacheEntry(member, dsd, false);
+                        entry.setName(dsd.getName());
+                        m_dsdCache.put(getKey(key, contextId), entry);
+                    }
+                }
+            }
+        }
+        else
+        {
+            // Seed the cache with dummy entry for Role that does not have DSD:
+            String key = buildKey(EMPTY_ELEMENT, roleName);
+            SDSet sdSet = new SDSet();
+            sdSet.setType(SDSet.SDType.DYNAMIC);
+            sdSet.setName(key);
+            sdSet.setMember(roleName);
+            sdSet.setContextId(contextId);
+            DsdCacheEntry entry = new DsdCacheEntry(roleName, sdSet, true);
+            entry.setName(key);
+            m_dsdCache.put(getKey(sdSet.getName(), contextId), entry);
+        }
+        return finalSet;
+    }
+
+    /**
+     * Given entry name, clear its corresponding object value from the cache.
+     *
+     * @param name contains the name of object to be cleared.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    static void clearSsdCacheEntry(String name, String contextId)
+    {
+        contextId = getContextId(contextId);
+        m_ssdCache.clear(getKey(name, contextId));
+    }
+
+    /**
+     * Get the matching SSD's from directory and add to the cache (if found).
+     *
+     * @param name of Role is used to search directory for matching SSD's.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return List of SSD's who have matching Role member.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    private static List<SDSet> putSsdCache(String name, String contextId)
+        throws SecurityException
+    {
+        Role role = new Role(name);
+        role.setContextId(contextId);
+        List<SDSet> ssdSets = sp.search(role, SDSet.SDType.STATIC);
+        m_ssdCache.put(getKey(name, contextId), ssdSets);
+        return ssdSets;
+    }
+
+    /**
+     * Look in cache for matching List of SSD's.
+     *
+     * @param name of Role is used to search directory for matching SSD's.
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return List of SSD's who have matching Role member.
+     * @throws SecurityException in the event of system or rule violation.
+     */
+    private static List<SDSet> getSsdCache(String name, String contextId)
+        throws SecurityException
+    {
+        List<SDSet> ssdSets = (List<SDSet>) m_ssdCache.get(getKey(name, contextId));
+        if (ssdSets == null)
+        {
+            ssdSets = putSsdCache(name, contextId);
+        }
+        return ssdSets;
+    }
+
+    /**
+     *
+     * @param parm1
+     * @param parm2
+     * @return
+     */
+    private static String buildKey(String parm1, String parm2)
+    {
+        return parm1 + ":" + parm2;
+    }
+
+    /**
+     *
+     * @param name
+     * @param contextId
+     * @return
+     */
+    private static String getKey(String name, String contextId)
+    {
+        contextId = getContextId(contextId);
+        return name += ":" + contextId;
+    }
+
+    /**
+     *
+     * @param contextId
+     * @return
+     */
+    private static String getContextId(String contextId)
+    {
+        String szContextId = GlobalIds.HOME;
+        if( StringUtils.isNotEmpty( contextId ) && !contextId.equals(GlobalIds.NULL))
+        {
+            szContextId = contextId;
+        }
+        return szContextId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java b/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
new file mode 100755
index 0000000..2d2d75a
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
@@ -0,0 +1,653 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.fortress.core.model.SDSet;
+import org.apache.directory.fortress.core.util.ObjUtil;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.fortress.core.CreateException;
+import org.apache.directory.fortress.core.FinderException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.model.ObjectFactory;
+import org.apache.directory.fortress.core.RemoveException;
+import org.apache.directory.fortress.core.UpdateException;
+import org.apache.directory.fortress.core.ldap.ApacheDsDataProvider;
+import org.apache.directory.fortress.core.model.Role;
+
+
+/**
+ * This class performs persistence on the RBAC Static Separation of Duties and Dynamic Separation of Duties data sets.
+ * <p/>
+ * The Fortress SDSet entity is a composite of the following other Fortress structural and aux object classes:
+ * <h4>1. organizationalRole Structural Object Class is used to store basic attributes like cn and description</h4>
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass ( 2.5.6.8 NAME 'organizationalRole'</code>
+ * <li> <code>DESC 'RFC2256: an organizational role'</code>
+ * <li> <code>SUP top STRUCTURAL</code>
+ * <li> <code>MUST cn</code>
+ * <li> <code>MAY ( x121Address $ registeredAddress $ destinationIndicator $</code>
+ * <li> <code>preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $</code>
+ * <li> <code>telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $</code>
+ * <li> <code>seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $</code>
+ * <li> <code>postOfficeBox $ postalCode $ postalAddress $</code>
+ * <li> <code>physicalDeliveryOfficeName $ ou $ st $ l $ description ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>2. The RBAC Separation of Duties</h4>
+ * <ul>
+ * <li>  ---Static Separation of Duties Set-------
+ * <li> <code>objectclass    ( 1.3.6.1.4.1.38088.2.4</code>
+ * <li> <code>NAME 'ftSSDSet'</code>
+ * <li> <code>DESC 'Fortress Role Static Separation of Duty Set Object Class'</code>
+ * <li> <code>SUP organizationalrole</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId $ ftSetName $ ftSetCardinality )</code>
+ * <li> <code>MAY ( ftRoles $ description ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <p/>
+ * OR
+ * <h4>Dynamic Separation of Duties Set</h4>
+ * <ul>
+ * <li>
+ * <li> <code>objectclass    ( 1.3.6.1.4.1.38088.2.5</code>
+ * <li> <code>NAME 'ftDSDSet'</code>
+ * <li> <code>DESC 'Fortress Role Dynamic Separation of Duty Set Object Class'</code>
+ * <li> <code>SUP organizationalrole</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId $ ftSetName $ ftSetCardinality )</code>
+ * <li> <code>MAY ( ftRoles $ description ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>3. ftMods AUXILIARY Object Class is used to store Fortress audit variables on target entity</h4>
+ * <ul>
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.4</code>
+ * <li> <code>NAME 'ftMods'</code>
+ * <li> <code>DESC 'Fortress Modifiers AUX Object Class'</code>
+ * <li> <code>AUXILIARY</code>
+ * <li> <code>MAY (</code>
+ * <li> <code>ftModifier $</code>
+ * <li> <code>ftModCode $</code>
+ * <li> <code>ftModId ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+final class SdDAO extends ApacheDsDataProvider
+{
+    private static final String SD_SET_NM = "ftSetName";
+    private static final String ROLES = "ftRoles";
+    private static final String SD_SET_CARDINALITY = "ftSetCardinality";
+
+    private static final String SSD_OBJECT_CLASS_NM = "ftSSDSet";
+    private static final String SSD_OBJ_CLASS[] =
+        {
+            SchemaConstants.TOP_OC, SSD_OBJECT_CLASS_NM, GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+    };
+
+    private static final String DSD_OBJECT_CLASS_NM = "ftDSDSet";
+    private static final String DSD_OBJ_CLASS[] =
+        {
+            SchemaConstants.TOP_OC, DSD_OBJECT_CLASS_NM, GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+    };
+
+    private static final String[] SD_SET_ATRS =
+        {
+            GlobalIds.FT_IID, SD_SET_NM, SchemaConstants.DESCRIPTION_AT, ROLES, SD_SET_CARDINALITY
+    };
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws org.apache.directory.fortress.core.CreateException
+     */
+    SDSet create( SDSet entity ) throws CreateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity.getName(), entity.getContextId() );
+        String[] objectClass = SSD_OBJ_CLASS;
+
+        if ( entity.getType() == SDSet.SDType.DYNAMIC )
+        {
+            objectClass = DSD_OBJ_CLASS;
+        }
+
+        try
+        {
+            Entry entry = new DefaultEntry( dn );
+            entry.add( createAttributes( SchemaConstants.OBJECT_CLASS_AT, objectClass ) );
+            entity.setId();
+            entry.add( GlobalIds.FT_IID, entity.getId() );
+            entry.add( SD_SET_NM, entity.getName() );
+
+            // description field is optional on this object class:
+            if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+            {
+                entry.add( SchemaConstants.DESCRIPTION_AT, entity.getDescription() );
+            }
+
+            // CN attribute is required for this object class:
+            entry.add( SchemaConstants.CN_AT, entity.getName() );
+            loadAttrs( entity.getMembers(), entry, ROLES );
+            entry.add( SD_SET_CARDINALITY, "" + entity.getCardinality() );
+
+            ld = getAdminConnection();
+            add( ld, entry, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "create SD set name [" + entity.getName() + "] type [" + entity.getType()
+                + "] caught LdapException=" + e.getMessage();
+            int errCode;
+            if ( entity.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_ADD_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_ADD_FAILED;
+            }
+
+            throw new CreateException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return entity;
+    }
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws org.apache.directory.fortress.core.UpdateException
+     */
+    SDSet update( SDSet entity ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity.getName(), entity.getContextId() );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+
+            if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, SchemaConstants.DESCRIPTION_AT, entity.getDescription() ) );
+            }
+
+            if ( entity.getCardinality() != null )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, SD_SET_CARDINALITY, entity.getCardinality().toString() ) );
+            }
+
+            loadAttrs( entity.getMembers(), mods, ROLES );
+
+            if ( mods.size() > 0 )
+            {
+                ld = getAdminConnection();
+                modify( ld, dn, mods, entity );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "update name [" + entity.getName() + "] type [" + entity.getType()
+                + "] caught LdapException=" + e.getMessage();
+            int errCode;
+            if ( entity.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_UPDATE_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_UPDATE_FAILED;
+            }
+
+            throw new UpdateException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param entity
+     * @throws org.apache.directory.fortress.core.RemoveException
+     */
+    SDSet remove( SDSet entity ) throws RemoveException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity.getName(), entity.getContextId() );
+
+        try
+        {
+            ld = getAdminConnection();
+            delete( ld, dn, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "remove SD name=" + entity.getName() + " type [" + entity.getType() + "] LdapException="
+                + e.getMessage();
+            int errCode;
+            if ( entity.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_DELETE_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_DELETE_FAILED;
+            }
+
+            throw new RemoveException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param sdSet
+     * @return
+     * @throws FinderException
+     */
+    SDSet getSD( SDSet sdSet ) throws FinderException
+    {
+        SDSet entity = null;
+        LdapConnection ld = null;
+        String dn = getDn( sdSet.getName(), sdSet.getContextId() );
+
+        try
+        {
+            ld = getAdminConnection();
+            Entry findEntry = read( ld, dn, SD_SET_ATRS );
+            if ( findEntry == null )
+            {
+                String warning = "getSD no entry found dn [" + dn + "]";
+                throw new FinderException( GlobalErrIds.SSD_NOT_FOUND, warning );
+            }
+            entity = unloadLdapEntry( findEntry, 0 );
+        }
+        catch ( LdapNoSuchObjectException e )
+        {
+            String warning = "getSD Obj COULD NOT FIND ENTRY for dn [" + dn + "]";
+            throw new FinderException( GlobalErrIds.SSD_NOT_FOUND, warning );
+        }
+        catch ( LdapException e )
+        {
+            String error = "getSSD dn [" + dn + "] LEXCD=" + e;
+            int errCode;
+
+            if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_READ_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_READ_FAILED;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * Given an SSD name and type, find matching object in the directory.
+     * @param sdset requires name and type.
+     * @return List of matching SDSets.
+     * @throws org.apache.directory.fortress.core.FinderException
+     */
+    List<SDSet> search( SDSet sdset ) throws FinderException
+    {
+        List<SDSet> sdList = new ArrayList<>();
+        LdapConnection ld = null;
+        String ssdRoot = getSdRoot( sdset.getContextId() );
+        String objectClass = SSD_OBJECT_CLASS_NM;
+
+        if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+        {
+            objectClass = DSD_OBJECT_CLASS_NM;
+        }
+
+        try
+        {
+            String searchVal = encodeSafeText( sdset.getName(), GlobalIds.ROLE_LEN );
+            String filter = GlobalIds.FILTER_PREFIX + objectClass + ")(" + SD_SET_NM + "=" + searchVal + "*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, ssdRoot,
+                SearchScope.SUBTREE, filter, SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "search sdset name [" + sdset.getName() + "] type [" + sdset.getType()
+                + "] caught LdapException=" + e.getMessage();
+            int errCode;
+
+            if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "search sdset name [" + sdset.getName() + "] type [" + sdset.getType()
+                + "] caught CursorException=" + e.getMessage();
+            int errCode;
+
+            if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return sdList;
+    }
+
+
+    /**
+     * @param role
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     */
+    List<SDSet> search( Role role, SDSet.SDType type ) throws FinderException
+    {
+        List<SDSet> sdList = new ArrayList<>();
+        LdapConnection ld = null;
+        String ssdRoot = getSdRoot( role.getContextId() );
+        String objectClass = SSD_OBJECT_CLASS_NM;
+        if ( type == SDSet.SDType.DYNAMIC )
+        {
+            objectClass = DSD_OBJECT_CLASS_NM;
+        }
+        try
+        {
+            String roleVal = encodeSafeText( role.getName(), GlobalIds.ROLE_LEN );
+            StringBuilder filterbuf = new StringBuilder();
+            filterbuf.append( GlobalIds.FILTER_PREFIX );
+            filterbuf.append( objectClass );
+            filterbuf.append( ")(" );
+
+            // Include any parents target role may have:
+            Set<String> roles = RoleUtil.getAscendants( role.getName(), role.getContextId() );
+
+            if ( ObjUtil.isNotNullOrEmpty( roles ) )
+            {
+                filterbuf.append( "|(" );
+                filterbuf.append( ROLES );
+                filterbuf.append( "=" );
+                filterbuf.append( roleVal );
+                filterbuf.append( ")" );
+
+                for ( String uRole : roles )
+                {
+                    filterbuf.append( "(" );
+                    filterbuf.append( ROLES );
+                    filterbuf.append( "=" );
+                    filterbuf.append( uRole );
+                    filterbuf.append( ")" );
+                }
+                filterbuf.append( ")" );
+
+            }
+            else
+            {
+                filterbuf.append( ROLES );
+                filterbuf.append( "=" );
+                filterbuf.append( roleVal );
+                filterbuf.append( ")" );
+
+            }
+
+            filterbuf.append( ")" );
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, ssdRoot,
+                SearchScope.SUBTREE, filterbuf.toString(), SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+
+            long sequence = 0;
+            while ( searchResults.next() )
+            {
+                sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "search role [" + role.getName() + "] type [" + type + "] caught LdapException="
+                + e.getMessage();
+            int errCode;
+
+            if ( type == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "search role [" + role.getName() + "] type [" + type + "] caught CursorException="
+                + e.getMessage();
+            int errCode;
+
+            if ( type == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return sdList;
+    }
+
+
+    /**
+     * @param roles
+     * @param sdSet
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     */
+    Set<SDSet> search( Set<String> roles, SDSet sdSet ) throws FinderException
+    {
+        Set<SDSet> sdList = new HashSet<>();
+        LdapConnection ld = null;
+        String ssdRoot = getSdRoot( sdSet.getContextId() );
+        String objectClass = SSD_OBJECT_CLASS_NM;
+
+        if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+        {
+            objectClass = DSD_OBJECT_CLASS_NM;
+        }
+
+        try
+        {
+            if ( ObjUtil.isNotNullOrEmpty( roles ) )
+            {
+                StringBuilder filterbuf = new StringBuilder();
+                filterbuf.append( GlobalIds.FILTER_PREFIX );
+                filterbuf.append( objectClass );
+                filterbuf.append( ")(|" );
+
+                for ( String rle : roles )
+                {
+                    filterbuf.append( "(" );
+                    filterbuf.append( ROLES );
+                    filterbuf.append( "=" );
+                    filterbuf.append( rle );
+                    filterbuf.append( ")" );
+                }
+                filterbuf.append( "))" );
+                ld = getAdminConnection();
+                SearchCursor searchResults = search( ld, ssdRoot,
+                    SearchScope.SUBTREE, filterbuf.toString(), SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+                long sequence = 0;
+
+                while ( searchResults.next() )
+                {
+                    sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+                }
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "search type [" + sdSet.getType() + "] caught LdapException=" + e.getMessage();
+            int errCode;
+
+            if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+            throw new FinderException( errCode, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "search type [" + sdSet.getType() + "] caught CursorException=" + e.getMessage();
+            int errCode;
+
+            if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+            {
+                errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+            }
+            else
+            {
+                errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+            }
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return sdList;
+    }
+
+
+    /**
+     * @param le
+     * @return
+     * @throws LdapInvalidAttributeValueException 
+     * @throws LdapException
+     */
+    private SDSet unloadLdapEntry( Entry le, long sequence ) throws LdapInvalidAttributeValueException
+    {
+        SDSet entity = new ObjectFactory().createSDset();
+        entity.setSequenceId( sequence );
+        entity.setId( getAttribute( le, GlobalIds.FT_IID ) );
+        entity.setName( getAttribute( le, SD_SET_NM ) );
+        entity.setDescription( getAttribute( le, SchemaConstants.DESCRIPTION_AT ) );
+        entity.setMembers( getAttributeSet( le, ROLES ) );
+        String szCard = getAttribute( le, SD_SET_CARDINALITY );
+        entity.setCardinality( Integer.valueOf( szCard ) );
+
+        return entity;
+    }
+
+
+    private String getDn( String name, String contextId )
+    {
+        return SchemaConstants.CN_AT + "=" + name + "," + getSdRoot( contextId );
+    }
+
+
+    private String getSdRoot( String contextId )
+    {
+        return getRootDn( contextId, GlobalIds.SD_ROOT );
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SdP.java b/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
new file mode 100755
index 0000000..a107231
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
@@ -0,0 +1,221 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.SDSet;
+import org.apache.directory.fortress.core.model.VUtil;
+
+
+/**
+ * Process module for Separation of Duty Relation data sets. The Fortress SD data set can be of two types:
+ * <ol>
+ * <li>Static Separation of Duties (SSD)</li>
+ * <li>Dynamic Separation of Duties (DSD)</li>
+ * </ol>
+ * The SDSet entity itself distinguishes which is being targeted by {@link org.apache.directory.fortress.core.model.SDSet.SDType} which is equal to {@link org.apache.directory.fortress.core.model.SDSet.SDType#STATIC} or {@link org.apache.directory.fortress.core.model.SDSet.SDType#DYNAMIC}.
+ * This class performs data validations and error mapping in addition to calling DAO methods.  It is typically called
+ * by internal Fortress Manager classes ({@link org.apache.directory.fortress.core.AdminMgr}, {@link org.apache.directory.fortress.core.ReviewMgr}) and also by internal SD utils.
+ * This class is not intended to be called externally or outside of Fortress Core itself.  This class will accept {@link org.apache.directory.fortress.core.model.SDSet},
+ * validate its contents and forward on to it's corresponding DAO {@link org.apache.directory.fortress.core.impl.SdDAO}.
+ * <p>
+ * Class will throw {@link SecurityException} to caller in the event of security policy, data constraint violation or system
+ * error internal to DAO object. This class will forward DAO exceptions ({@link org.apache.directory.fortress.core.FinderException},
+ * {@link org.apache.directory.fortress.core.CreateException},{@link org.apache.directory.fortress.core.UpdateException},{@link org.apache.directory.fortress.core.RemoveException}),
+ * or {@link org.apache.directory.fortress.core.ValidationException} as {@link SecurityException}s with appropriate
+ * error id from {@link org.apache.directory.fortress.core.GlobalErrIds}.
+ * <p>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public final class SdP
+{
+    /**
+     * Get the DAO created:
+     */
+    private static final SdDAO sdDao = new SdDAO();
+
+
+    /**
+     * Package private constructor.
+     */
+    SdP()
+    {
+    }
+
+
+    /**
+     * Adds a new SDSet to directory. The OrgUnit SDType enum will determine which data set insertion will
+     * occur - STATIC or DYNAMIC.  The SDSet entity input will be validated to ensure that:
+     * name is present, and reasonability checks on all of the other populated values.
+     *
+     * @param entity SDSet contains data targeted for insertion.
+     * @return SDSet entity copy of input + additional attributes (internalId) that were added by op.
+     * @throws SecurityException in the event of data validation or DAO system error.
+     */
+    SDSet add( SDSet entity ) throws SecurityException
+    {
+        validate( entity );
+        return sdDao.create( entity );
+    }
+
+
+    /**
+     * Updates existing SDSet in directory. The SDSet type enum will determine which data set insertion will
+     * occur - STATIC or DYNAMIC.  The SDSet entity input will be validated to ensure that:
+     * name is present, and reasonability checks on all of the other populated values.
+     * Null or empty attributes are ignored.
+     *
+     * @param entity SDSet contains data targeted for updating.  Null attributes ignored.
+     * @return SDSet entity copy of input + additional attributes (internalId) that were updated by op.
+     * @throws SecurityException in the event of data validation or DAO system error.
+     */
+    SDSet update( SDSet entity ) throws SecurityException
+    {
+        validate( entity );
+        return sdDao.update( entity );
+    }
+
+
+    /**
+     * This method performs a "hard" delete.  It completely the SDSet node from the ldap directory.
+     * The SDSet type enum will determine where deletion will occur - STATIC or DYNAMIC data sets.
+     * SDSet entity must exist in directory prior to making this call else exception will be thrown.
+     *
+     * @param entity Contains the name of the SDSet node targeted for deletion.
+     * @return SDSet is a copy of entity.
+     * @throws SecurityException in the event of data validation or DAO system error.
+     */
+    SDSet delete( SDSet entity ) throws SecurityException
+    {
+        return sdDao.remove( entity );
+    }
+
+
+    /**
+     * Return a fully populated SDSet entity for a given STATIC or DYNAMIC SDSet name.  If matching record not found a
+     * SecurityException will be thrown.
+     *
+     * @param entity contains full SDSet name used for STATIC or DYNAMIC data sets in directory.
+     * @return SDSet entity containing all attributes associated with ou in directory.
+     * @throws SecurityException in the event SDSet not found or DAO search error.
+     */
+    SDSet read( SDSet entity ) throws SecurityException
+    {
+        SDSet sde;
+        // The assumption is this method is called from ReviewMgr.ssdRoleSetRoles or ReviewMgr.dsdRoleSetRoles.
+        // If called from ReviewMgr, the object class type will be passed in:
+        SDSet.SDType type = entity.getType();
+        sde = sdDao.getSD( entity );
+        // Load the previously saved type onto the return entity:
+        sde.setType( type );
+        return sde;
+    }
+
+
+    /**
+     * Will search using a single RBAC Role name either STATIC or DYNAMIC SDSet depending on which type is passed.
+     * The role entity contains full RBAC Role name associated with SDSet node in directory.
+     *
+     * @param sdSet contains sdset name or partial name along with sdset type of STATIC or DYNAMIC.
+     * @return List of SDSet entities found.
+     * @throws SecurityException in the event of DAO search error.
+     */
+    List<SDSet> search( SDSet sdSet ) throws SecurityException
+    {
+        return sdDao.search( sdSet );
+    }
+
+
+    /**
+     * Will search using a single RBAC Role name either STATIC or DYNAMIC SDSet depending on which type is passed.
+     * The role entity contains full RBAC Role name associated with SDSet node in directory.
+     *
+     * @param role contains full role name associated with SDSet.
+     * @param type either STATIC or DYNAMIC depending on target search data set.
+     * @return List of SDSet entities found.
+     * @throws SecurityException in the event of DAO search error.
+     */
+    List<SDSet> search( Role role, SDSet.SDType type ) throws SecurityException
+    {
+        return sdDao.search( role, type );
+    }
+
+
+    /**
+     * Will search using list of input RBAC role names either STATIC or DYNAMIC SDSet depending on which type is passed.
+     * The role entity contains full RBAC Role name associated with SDSet node in directory.
+     *
+     * @param rls  contains set of type String containing full role names associated with SDSet.
+     * @param sdSet contains type either STATIC or DYNAMIC depending on target search data set.
+     * @return List of SDSet entities found.
+     * @throws SecurityException in the event of DAO search error.
+     */
+    Set<SDSet> search( Set<String> rls, SDSet sdSet ) throws SecurityException
+    {
+        return sdDao.search( rls, sdSet );
+    }
+
+
+    /**
+     * Method will perform simple validations to ensure the integrity of the SDSet entity targeted for insertion
+     * or updating in directory.  This method will ensure the name and type enum are specified.  Method will
+     * also ensure every Role name set is valid RBAC role entity in directory.  It will also perform
+     * reasonability check on description if set.
+     *
+     * @param entity contains the enum type to validate
+     * @throws SecurityException thrown in the event the attribute is null.
+     */
+    private void validate( SDSet entity )
+        throws SecurityException
+    {
+        // TODO: Add more validations here:
+        VUtil.safeText( entity.getName(), GlobalIds.OU_LEN );
+        if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+        {
+            VUtil.description( entity.getDescription() );
+        }
+        Set<String> roles = entity.getMembers();
+        if ( roles != null )
+        {
+            RoleP rp = new RoleP();
+            for ( String key : roles )
+            {
+                // when removing last role member a placeholder must be left in data set:
+                if ( !key.equalsIgnoreCase( GlobalIds.NONE ) )
+                {
+                    // Ensure the name exists:
+                    Role role = new Role( key );
+                    role.setContextId( entity.getContextId() );
+                    rp.read( role );
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java b/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
new file mode 100755
index 0000000..4db75a6
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
@@ -0,0 +1,77 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Shawn McKinney
+ * Date: 1/21/12
+ * Time: 7:59 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class SetAdapter extends XmlAdapter<ArrayList<String>, Set<String>>
+{
+    public Set<String> unmarshal(ArrayList<String> val) throws Exception
+    {
+        Set<String> members = null;
+        if(val != null)
+        {
+            members = new TreeSet<>();
+            for(String member : val)
+            {
+                members.add(member);
+            }
+        }
+        return members;
+    }
+
+    public ArrayList<String> marshal(Set<String> val) throws Exception
+    {
+        ArrayList<String> members = null;
+        if(val != null)
+        {
+            members = new ArrayList<>();
+            for(String member : val)
+            {
+                members.add(member);
+            }
+        }
+        return members;
+    }
+}
+
+
+/*
+    public char[] unmarshal(String val) throws Exception
+    {
+        return val.toCharArray();
+    }
+
+    public String marshal(char[] val) throws Exception
+    {
+        return val.toString();
+    }
+
+ */


Mime
View raw message