001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core;
021    
022    
023    import java.util.Set;
024    import java.util.concurrent.locks.ReentrantReadWriteLock;
025    
026    import javax.naming.directory.SearchControls;
027    
028    import org.apache.directory.server.core.filtering.EntryFilteringCursor;
029    import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
030    import org.apache.directory.server.core.partition.PartitionNexus;
031    import org.apache.directory.shared.ldap.constants.SchemaConstants;
032    import org.apache.directory.shared.ldap.entry.StringValue;
033    import org.apache.directory.shared.ldap.entry.ServerEntry;
034    import org.apache.directory.shared.ldap.exception.LdapException;
035    import org.apache.directory.shared.ldap.filter.EqualityNode;
036    import org.apache.directory.shared.ldap.filter.ExprNode;
037    import org.apache.directory.shared.ldap.message.AliasDerefMode;
038    import org.apache.directory.shared.ldap.name.DN;
039    import org.apache.directory.shared.ldap.util.tree.DnBranchNode;
040    
041    
042    /**
043     * Implement a referral Manager, handling the requests from the LDAP protocol.
044     * <br>
045     * Referrals are stored in a tree, where leaves are the referrals. We are using 
046     * the very same structure than for the partition manager.
047     *
048     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
049     * @version $Rev$, $Date$
050     */
051    public class ReferralManagerImpl implements ReferralManager
052    {
053        /** The referrals tree */
054        private DnBranchNode<ServerEntry> referrals;
055    
056        /** A lock to guarantee the manager consistency */
057        private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock();
058    
059        
060        /**
061         * 
062         * Creates a new instance of ReferralManagerImpl.
063         *
064         * @param directoryService The directory service
065         * @throws Exception If we can't initialize the manager
066         */
067        public ReferralManagerImpl( DirectoryService directoryService ) throws Exception
068        {
069            lockWrite();
070            
071            referrals = new DnBranchNode<ServerEntry>();
072            PartitionNexus nexus = directoryService.getPartitionNexus();
073    
074            Set<String> suffixes = nexus.listSuffixes( null );
075            
076            init( directoryService, suffixes.toArray( new String[]{} ) );
077            
078            unlock();
079        }
080        
081        
082        /**
083         * Get a read-lock on the referralManager. 
084         * No read operation can be done on the referralManager if this
085         * method is not called before.
086         */
087        public void lockRead()
088        {
089            mutex.readLock().lock();
090        }
091        
092        
093        /**
094         * Get a write-lock on the referralManager. 
095         * No write operation can be done on the referralManager if this
096         * method is not called before.
097         */
098        public void lockWrite()
099        {
100            mutex.writeLock().lock();
101        }
102    
103        
104        /**
105         * Release the read-write lock on the referralManager. 
106         * This method must be called after having read or modified the
107         * ReferralManager
108         */
109        public void unlock()
110        {
111            if ( mutex.isWriteLockedByCurrentThread() )
112            {
113                mutex.writeLock().unlock();
114            }
115            else
116            {
117                mutex.readLock().unlock();
118            }
119        }
120        
121        
122        /**
123         * {@inheritDoc}
124         */
125        public void addReferral( ServerEntry entry )
126        {
127            try
128            {
129                ((DnBranchNode<ServerEntry>)referrals).add( entry.getDn(), entry );
130            }
131            catch ( LdapException ne )
132            {
133                // Do nothing
134            }
135        }
136    
137    
138        /**
139         * {@inheritDoc}
140         */
141        public void init( DirectoryService directoryService, String... suffixes ) throws Exception
142        {
143            ExprNode referralFilter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 
144                new StringValue( SchemaConstants.REFERRAL_OC ) );
145    
146            // Lookup for each entry with the ObjectClass = Referral value
147            SearchControls searchControl = new SearchControls();
148            searchControl.setReturningObjFlag( false );
149            searchControl.setSearchScope( SearchControls.SUBTREE_SCOPE );
150            
151            CoreSession adminSession = directoryService.getAdminSession();
152            PartitionNexus nexus = directoryService.getPartitionNexus();
153    
154            for ( String suffix:suffixes )
155            {
156                // We will store each entry's DN into the Referral tree
157                DN suffixDn = new DN( suffix );
158                suffixDn.normalize( directoryService.getSchemaManager().getNormalizerMapping() );
159                
160                SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffixDn, referralFilter, searchControl );
161                searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS );
162                EntryFilteringCursor cursor = nexus.search( searchOperationContext );
163                
164                // Move to the first entry in the cursor
165                cursor.beforeFirst();
166                
167                while ( cursor.next() ) 
168                {
169                    ServerEntry entry = cursor.get();
170    
171                    // Lock the referralManager
172                    lockWrite();
173                    
174                    // Add it at the right place
175                    addReferral( entry );
176                    
177                    // Unlock the referralManager
178                    unlock();
179                }
180            }
181        }
182        
183        
184        /**
185         * {@inheritDoc}
186         */
187        public void remove( DirectoryService directoryService, DN suffix ) throws Exception
188        {
189            ExprNode referralFilter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 
190                new StringValue( SchemaConstants.REFERRAL_OC ) );
191    
192            // Lookup for each entry with the ObjectClass = Referral value
193            SearchControls searchControl = new SearchControls();
194            searchControl.setReturningObjFlag( false );
195            searchControl.setSearchScope( SearchControls.SUBTREE_SCOPE );
196            
197            CoreSession adminSession = directoryService.getAdminSession();
198            PartitionNexus nexus = directoryService.getPartitionNexus();
199    
200            // We will store each entry's DN into the Referral tree
201            SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffix,
202                referralFilter, searchControl );
203            searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS );
204            EntryFilteringCursor cursor = nexus.search( searchOperationContext );
205            
206            // Move to the first entry in the cursor
207            cursor.beforeFirst();
208            
209            while ( cursor.next() ) 
210            {
211                ServerEntry entry = cursor.get();
212                
213                // Add it at the right place
214                removeReferral( entry );
215            }
216        }
217        
218        
219        /**
220         * {@inheritDoc}
221         */
222        public boolean hasParentReferral( DN dn )
223        {
224            return referrals.hasParentElement( dn );
225        }
226    
227    
228        /**
229         * {@inheritDoc}
230         */
231        public ServerEntry getParentReferral( DN dn )
232        {
233            if ( !hasParentReferral( dn ) )
234            {
235                return null;
236            }
237            
238            return referrals.getParentElement( dn );
239        }
240    
241        
242        /**
243         * {@inheritDoc}
244         */
245        public boolean isReferral( DN dn )
246        {
247            ServerEntry parent = referrals.getParentElement( dn );
248            
249            if ( parent != null )
250            {
251                return dn.equals( parent.getDn() );
252            }
253            else
254            {
255                return false;
256            }
257        }
258    
259    
260        /**
261         * {@inheritDoc}
262         */
263        public void removeReferral( ServerEntry entry )
264        {
265            referrals.remove( entry );
266        }
267    }