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 */ 020package org.apache.directory.server.core.shared; 021 022 023import java.io.IOException; 024import java.util.Set; 025import java.util.concurrent.locks.ReentrantReadWriteLock; 026 027import javax.naming.directory.SearchControls; 028 029import org.apache.directory.api.ldap.model.constants.SchemaConstants; 030import org.apache.directory.api.ldap.model.entry.Entry; 031import org.apache.directory.api.ldap.model.entry.Value; 032import org.apache.directory.api.ldap.model.exception.LdapException; 033import org.apache.directory.api.ldap.model.exception.LdapOperationException; 034import org.apache.directory.api.ldap.model.exception.LdapOtherException; 035import org.apache.directory.api.ldap.model.filter.EqualityNode; 036import org.apache.directory.api.ldap.model.filter.ExprNode; 037import org.apache.directory.api.ldap.model.message.AliasDerefMode; 038import org.apache.directory.api.ldap.model.name.Dn; 039import org.apache.directory.api.ldap.model.schema.AttributeType; 040import org.apache.directory.api.ldap.util.tree.DnNode; 041import org.apache.directory.server.core.api.CoreSession; 042import org.apache.directory.server.core.api.DirectoryService; 043import org.apache.directory.server.core.api.ReferralManager; 044import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; 045import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; 046import org.apache.directory.server.core.api.partition.Partition; 047import org.apache.directory.server.core.api.partition.PartitionNexus; 048import org.apache.directory.server.core.api.partition.PartitionTxn; 049 050 051/** 052 * Implement a referral Manager, handling the requests from the LDAP protocol. 053 * <br> 054 * Referrals are stored in a tree, where leaves are the referrals. We are using 055 * the very same structure than for the partition manager. 056 * 057 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 058 */ 059public class ReferralManagerImpl implements ReferralManager 060{ 061 /** The referrals tree */ 062 private DnNode<Entry> referrals; 063 064 /** A lock to guarantee the manager consistency */ 065 private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock(); 066 067 /** A storage for the ObjectClass attributeType */ 068 private AttributeType objectClassAT; 069 070 071 /** 072 * 073 * Creates a new instance of ReferralManagerImpl. 074 * 075 * @param directoryService The directory service 076 * @throws LdapException If we can't initialize the manager 077 */ 078 public ReferralManagerImpl( DirectoryService directoryService ) throws LdapException 079 { 080 lockWrite(); 081 082 try 083 { 084 referrals = new DnNode<>(); 085 PartitionNexus nexus = directoryService.getPartitionNexus(); 086 087 Set<String> suffixes = nexus.listSuffixes(); 088 objectClassAT = directoryService.getSchemaManager().getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); 089 090 init( directoryService, suffixes.toArray( new String[] 091 {} ) ); 092 } 093 finally 094 { 095 unlock(); 096 } 097 } 098 099 100 /** 101 * Get a read-lock on the referralManager. 102 * No read operation can be done on the referralManager if this 103 * method is not called before. 104 */ 105 @Override 106 public void lockRead() 107 { 108 mutex.readLock().lock(); 109 } 110 111 112 /** 113 * Get a write-lock on the referralManager. 114 * No write operation can be done on the referralManager if this 115 * method is not called before. 116 */ 117 @Override 118 public void lockWrite() 119 { 120 mutex.writeLock().lock(); 121 } 122 123 124 /** 125 * Release the read-write lock on the referralManager. 126 * This method must be called after having read or modified the 127 * ReferralManager 128 */ 129 @Override 130 public void unlock() 131 { 132 if ( mutex.isWriteLockedByCurrentThread() ) 133 { 134 mutex.writeLock().unlock(); 135 } 136 else 137 { 138 mutex.readLock().unlock(); 139 } 140 } 141 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override 147 public void addReferral( Entry entry ) 148 { 149 try 150 { 151 referrals.add( entry.getDn(), entry ); 152 } 153 catch ( LdapException ne ) 154 { 155 // Do nothing 156 } 157 } 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override 164 public void init( DirectoryService directoryService, String... suffixes ) throws LdapException 165 { 166 ExprNode referralFilter = new EqualityNode<String>( objectClassAT, 167 new Value( objectClassAT, SchemaConstants.REFERRAL_OC ) ); 168 169 // Lookup for each entry with the ObjectClass = Referral value 170 SearchControls searchControl = new SearchControls(); 171 searchControl.setReturningObjFlag( false ); 172 searchControl.setSearchScope( SearchControls.SUBTREE_SCOPE ); 173 174 CoreSession adminSession = directoryService.getAdminSession(); 175 PartitionNexus nexus = directoryService.getPartitionNexus(); 176 177 for ( String suffix : suffixes ) 178 { 179 // We will store each entry's Dn into the Referral tree 180 Dn suffixDn = directoryService.getDnFactory().create( suffix ); 181 182 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffixDn, 183 referralFilter, searchControl ); 184 185 Partition partition = nexus.getPartition( suffixDn ); 186 187 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() ) 188 { 189 searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS ); 190 searchOperationContext.setTransaction( partitionTxn ); 191 searchOperationContext.setPartition( partition ); 192 EntryFilteringCursor cursor = nexus.search( searchOperationContext ); 193 194 try 195 { 196 // Move to the first entry in the cursor 197 cursor.beforeFirst(); 198 199 while ( cursor.next() ) 200 { 201 Entry entry = cursor.get(); 202 203 // Lock the referralManager 204 lockWrite(); 205 206 try 207 { 208 // Add it at the right place 209 addReferral( entry ); 210 } 211 finally 212 { 213 // Unlock the referralManager 214 unlock(); 215 } 216 } 217 218 cursor.close(); 219 } 220 catch ( Exception e ) 221 { 222 throw new LdapOperationException( e.getMessage(), e ); 223 } 224 } 225 catch ( IOException ioe ) 226 { 227 throw new LdapOtherException( ioe.getMessage(), ioe ); 228 } 229 } 230 } 231 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override 237 public void remove( DirectoryService directoryService, Dn suffix ) throws Exception 238 { 239 ExprNode referralFilter = new EqualityNode<String>( objectClassAT, 240 new Value( objectClassAT, SchemaConstants.REFERRAL_OC ) ); 241 242 // Lookup for each entry with the ObjectClass = Referral value 243 SearchControls searchControl = new SearchControls(); 244 searchControl.setReturningObjFlag( false ); 245 searchControl.setSearchScope( SearchControls.SUBTREE_SCOPE ); 246 247 CoreSession adminSession = directoryService.getAdminSession(); 248 PartitionNexus nexus = directoryService.getPartitionNexus(); 249 250 // We will store each entry's Dn into the Referral tree 251 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffix, 252 referralFilter, searchControl ); 253 Partition partition = nexus.getPartition( suffix ); 254 searchOperationContext.setPartition( partition ); 255 searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS ); 256 257 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() ) 258 { 259 searchOperationContext.setTransaction( partitionTxn ); 260 EntryFilteringCursor cursor = nexus.search( searchOperationContext ); 261 262 // Move to the first entry in the cursor 263 cursor.beforeFirst(); 264 265 while ( cursor.next() ) 266 { 267 Entry entry = cursor.get(); 268 269 // Add it at the right place 270 removeReferral( entry ); 271 } 272 } 273 } 274 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 public boolean hasParentReferral( Dn dn ) 281 { 282 DnNode<Entry> referral = referrals.getNode( dn ); 283 284 return ( referral != null ) && referral.isLeaf(); 285 } 286 287 288 /** 289 * {@inheritDoc} 290 */ 291 @Override 292 public Entry getParentReferral( Dn dn ) 293 { 294 if ( !hasParentReferral( dn ) ) 295 { 296 return null; 297 } 298 299 return referrals.getElement( dn ); 300 } 301 302 303 /** 304 * {@inheritDoc} 305 */ 306 @Override 307 public boolean isReferral( Dn dn ) 308 { 309 Entry parent = referrals.getElement( dn ); 310 311 if ( parent != null ) 312 { 313 return dn.equals( parent.getDn() ); 314 } 315 else 316 { 317 return false; 318 } 319 } 320 321 322 /** 323 * {@inheritDoc} 324 */ 325 @Override 326 public void removeReferral( Entry entry ) throws LdapException 327 { 328 referrals.remove( entry.getDn() ); 329 } 330}