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.shared.ldap.schema.registries; 021 022 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.Map; 026 027 import org.apache.directory.shared.asn1.primitives.OID; 028 import org.apache.directory.shared.i18n.I18n; 029 import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException; 030 import org.apache.directory.shared.ldap.exception.LdapException; 031 import org.apache.directory.shared.ldap.schema.LoadableSchemaObject; 032 import org.apache.directory.shared.ldap.schema.SchemaObject; 033 import org.apache.directory.shared.ldap.schema.SchemaObjectType; 034 import org.apache.directory.shared.ldap.util.StringTools; 035 import org.slf4j.Logger; 036 import org.slf4j.LoggerFactory; 037 038 039 /** 040 * Common schema object registry interface. 041 * 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 * @version $Rev$, $Date$ 044 */ 045 public abstract class DefaultSchemaObjectRegistry<T extends SchemaObject> implements SchemaObjectRegistry<T>, 046 Iterable<T> 047 { 048 /** static class logger */ 049 private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaObjectRegistry.class ); 050 051 /** A speedup for debug */ 052 private static final boolean DEBUG = LOG.isDebugEnabled(); 053 054 /** a map of SchemaObject looked up by name */ 055 protected Map<String, T> byName; 056 057 /** The SchemaObject type, used by the toString() method */ 058 protected SchemaObjectType schemaObjectType; 059 060 /** the global OID Registry */ 061 protected OidRegistry oidRegistry; 062 063 064 /** 065 * Creates a new DefaultSchemaObjectRegistry instance. 066 */ 067 protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry oidRegistry ) 068 { 069 byName = new HashMap<String, T>(); 070 this.schemaObjectType = schemaObjectType; 071 this.oidRegistry = oidRegistry; 072 } 073 074 075 /** 076 * {@inheritDoc} 077 */ 078 public boolean contains( String oid ) 079 { 080 if ( !byName.containsKey( oid ) ) 081 { 082 return byName.containsKey( StringTools.toLowerCase( oid ) ); 083 } 084 085 return true; 086 } 087 088 089 /** 090 * {@inheritDoc} 091 */ 092 public String getSchemaName( String oid ) throws LdapException 093 { 094 if ( !OID.isOID( oid ) ) 095 { 096 String msg = I18n.err( I18n.ERR_04267 ); 097 LOG.warn( msg ); 098 throw new LdapException( msg ); 099 } 100 101 SchemaObject schemaObject = byName.get( oid ); 102 103 if ( schemaObject != null ) 104 { 105 return schemaObject.getSchemaName(); 106 } 107 108 String msg = I18n.err( I18n.ERR_04268, oid ); 109 LOG.warn( msg ); 110 throw new LdapException( msg ); 111 } 112 113 114 /** 115 * {@inheritDoc} 116 */ 117 public void renameSchema( String originalSchemaName, String newSchemaName ) 118 { 119 // Loop on all the SchemaObjects stored and remove those associated 120 // with the give schemaName 121 for ( T schemaObject : this ) 122 { 123 if ( originalSchemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) ) 124 { 125 schemaObject.setSchemaName( newSchemaName ); 126 127 if ( DEBUG ) 128 { 129 LOG.debug( "Renamed {} schemaName to {}", schemaObject, newSchemaName ); 130 } 131 } 132 } 133 } 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 public Iterator<T> iterator() 140 { 141 return ( Iterator<T> ) oidRegistry.iterator(); 142 } 143 144 145 /** 146 * {@inheritDoc} 147 */ 148 public Iterator<String> oidsIterator() 149 { 150 return byName.keySet().iterator(); 151 } 152 153 154 /** 155 * {@inheritDoc} 156 */ 157 public T lookup( String oid ) throws LdapException 158 { 159 if ( oid == null ) 160 { 161 return null; 162 } 163 164 T schemaObject = byName.get( oid ); 165 166 if ( schemaObject == null ) 167 { 168 // let's try with trimming and lowercasing now 169 schemaObject = byName.get( StringTools.trim( StringTools.toLowerCase( oid ) ) ); 170 } 171 172 if ( schemaObject == null ) 173 { 174 String msg = I18n.err( I18n.ERR_04269, schemaObjectType.name(), oid ); 175 LOG.debug( msg ); 176 throw new LdapException( msg ); 177 } 178 179 if ( DEBUG ) 180 { 181 LOG.debug( "Found {} with oid: {}", schemaObject, oid ); 182 } 183 184 return schemaObject; 185 } 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 public void register( T schemaObject ) throws LdapException 192 { 193 String oid = schemaObject.getOid(); 194 195 if ( byName.containsKey( oid ) ) 196 { 197 String msg = I18n.err( I18n.ERR_04270, schemaObjectType.name(), oid ); 198 LOG.warn( msg ); 199 throw new LdapAttributeInUseException( msg ); 200 } 201 202 byName.put( oid, schemaObject ); 203 204 /* 205 * add the aliases/names to the name map along with their toLowerCase 206 * versions of the name: this is used to make sure name lookups work 207 */ 208 for ( String name : schemaObject.getNames() ) 209 { 210 String lowerName = StringTools.trim( StringTools.toLowerCase( name ) ); 211 212 if ( byName.containsKey( lowerName ) ) 213 { 214 String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name ); 215 LOG.warn( msg ); 216 throw new LdapAttributeInUseException( msg ); 217 } 218 else 219 { 220 byName.put( lowerName, schemaObject ); 221 } 222 } 223 224 // And register the oid -> schemaObject relation 225 oidRegistry.register( schemaObject ); 226 227 if ( LOG.isDebugEnabled() ) 228 { 229 LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid ); 230 } 231 } 232 233 234 /** 235 * {@inheritDoc} 236 */ 237 public T unregister( String numericOid ) throws LdapException 238 { 239 if ( !OID.isOID( numericOid ) ) 240 { 241 String msg = I18n.err( I18n.ERR_04272, numericOid ); 242 LOG.error( msg ); 243 throw new LdapException( msg ); 244 } 245 246 T schemaObject = byName.remove( numericOid ); 247 248 for ( String name : schemaObject.getNames() ) 249 { 250 byName.remove( name ); 251 } 252 253 // And remove the SchemaObject from the oidRegistry 254 oidRegistry.unregister( numericOid ); 255 256 if ( DEBUG ) 257 { 258 LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid ); 259 } 260 261 return schemaObject; 262 } 263 264 265 /** 266 * {@inheritDoc} 267 */ 268 public T unregister( T schemaObject ) throws LdapException 269 { 270 String oid = schemaObject.getOid(); 271 272 if ( !byName.containsKey( oid ) ) 273 { 274 String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid ); 275 LOG.warn( msg ); 276 throw new LdapException( msg ); 277 } 278 279 // Remove the oid 280 T removed = byName.remove( oid ); 281 282 /* 283 * Remove the aliases/names from the name map along with their toLowerCase 284 * versions of the name. 285 */ 286 for ( String name : schemaObject.getNames() ) 287 { 288 byName.remove( StringTools.trim( StringTools.toLowerCase( name ) ) ); 289 } 290 291 // And unregister the oid -> schemaObject relation 292 oidRegistry.unregister( oid ); 293 294 return removed; 295 } 296 297 298 /** 299 * {@inheritDoc} 300 */ 301 public void unregisterSchemaElements( String schemaName ) throws LdapException 302 { 303 if ( schemaName == null ) 304 { 305 return; 306 } 307 308 // Loop on all the SchemaObjects stored and remove those associated 309 // with the give schemaName 310 for ( T schemaObject : this ) 311 { 312 if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) ) 313 { 314 String oid = schemaObject.getOid(); 315 SchemaObject removed = unregister( oid ); 316 317 if ( DEBUG ) 318 { 319 LOG.debug( "Removed {} with oid {} from the registry", removed, oid ); 320 } 321 } 322 } 323 } 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 public String getOidByName( String name ) throws LdapException 330 { 331 T schemaObject = byName.get( name ); 332 333 if ( schemaObject == null ) 334 { 335 // last resort before giving up check with lower cased version 336 String lowerCased = name.toLowerCase(); 337 338 schemaObject = byName.get( lowerCased ); 339 340 // ok this name is not for a schema object in the registry 341 if ( schemaObject == null ) 342 { 343 throw new LdapException( I18n.err( I18n.ERR_04274, name ) ); 344 } 345 } 346 347 // we found the schema object by key on the first lookup attempt 348 return schemaObject.getOid(); 349 } 350 351 352 /** 353 * {@inheritDoc} 354 */ 355 public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original ) 356 { 357 // Fill the byName and OidRegistry maps, the type has already be copied 358 for ( String key : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.keySet() ) 359 { 360 // Clone each SchemaObject 361 T value = ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.get( key ); 362 363 if ( value instanceof LoadableSchemaObject ) 364 { 365 // Update the data structure. 366 // Comparators, Normalizers and SyntaxCheckers aren't copied, 367 // they are immutable 368 byName.put( key, value ); 369 370 // Update the OidRegistry 371 oidRegistry.put( value ); 372 } 373 else 374 { 375 T copiedValue = null; 376 377 // Copy the value if it's not already in the oidRegistry 378 if ( oidRegistry.contains( value.getOid() ) ) 379 { 380 try 381 { 382 copiedValue = ( T ) oidRegistry.getSchemaObject( value.getOid() ); 383 } 384 catch ( LdapException ne ) 385 { 386 // Can't happen 387 } 388 } 389 else 390 { 391 copiedValue = ( T ) value.copy(); 392 } 393 394 // Update the data structure. 395 byName.put( key, copiedValue ); 396 397 // Update the OidRegistry 398 oidRegistry.put( copiedValue ); 399 } 400 } 401 402 return this; 403 } 404 405 406 /** 407 * {@inheritDoc} 408 */ 409 public SchemaObject get( String oid ) 410 { 411 try 412 { 413 return oidRegistry.getSchemaObject( oid ); 414 } 415 catch ( LdapException ne ) 416 { 417 return null; 418 } 419 } 420 421 422 /** 423 * {@inheritDoc} 424 */ 425 public SchemaObjectType getType() 426 { 427 return schemaObjectType; 428 } 429 430 431 /** 432 * {@inheritDoc} 433 */ 434 public int size() 435 { 436 return oidRegistry.size(); 437 } 438 439 440 /** 441 * @see Object#toString() 442 */ 443 public String toString() 444 { 445 StringBuilder sb = new StringBuilder(); 446 447 sb.append( schemaObjectType ).append( ": " ); 448 boolean isFirst = true; 449 450 for ( String name : byName.keySet() ) 451 { 452 if ( isFirst ) 453 { 454 isFirst = false; 455 } 456 else 457 { 458 sb.append( ", " ); 459 } 460 461 T schemaObject = byName.get( name ); 462 463 sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' ); 464 } 465 466 return sb.toString(); 467 } 468 469 470 /** 471 * {@inheritDoc} 472 */ 473 public void clear() 474 { 475 // Clear all the schemaObjects 476 for ( SchemaObject schemaObject : oidRegistry ) 477 { 478 // Don't clear LoadableSchemaObject 479 if ( !( schemaObject instanceof LoadableSchemaObject ) ) 480 { 481 schemaObject.clear(); 482 } 483 } 484 485 // Remove the byName elements 486 byName.clear(); 487 488 // Clear the OidRegistry 489 oidRegistry.clear(); 490 } 491 }