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.api.ldap.model.schema.registries; 021 022 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.Map; 026 027import org.apache.directory.api.asn1.util.Oid; 028import org.apache.directory.api.i18n.I18n; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.exception.LdapSchemaException; 031import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes; 032import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject; 033import org.apache.directory.api.ldap.model.schema.SchemaObject; 034import org.apache.directory.api.ldap.model.schema.SchemaObjectType; 035import org.apache.directory.api.util.Strings; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039 040/** 041 * Common schema object registry interface. 042 * 043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 044 */ 045public 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<T> oidRegistry; 062 063 064 /** 065 * Creates a new DefaultSchemaObjectRegistry instance. 066 */ 067 protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry<T> 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( Strings.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_NOT_FOUND, 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 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( Strings.trim( Strings.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 LdapSchemaException ldapSchemaException = new LdapSchemaException( 200 LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, msg ); 201 ldapSchemaException.setSourceObject( schemaObject ); 202 throw ldapSchemaException; 203 } 204 205 byName.put( oid, schemaObject ); 206 207 /* 208 * add the aliases/names to the name map along with their toLowerCase 209 * versions of the name: this is used to make sure name lookups work 210 */ 211 for ( String name : schemaObject.getNames() ) 212 { 213 String lowerName = Strings.trim( Strings.toLowerCase( name ) ); 214 215 if ( byName.containsKey( lowerName ) ) 216 { 217 String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name ); 218 LOG.warn( msg ); 219 LdapSchemaException ldapSchemaException = new LdapSchemaException( 220 LdapSchemaExceptionCodes.NAME_ALREADY_REGISTERED, msg ); 221 ldapSchemaException.setSourceObject( schemaObject ); 222 throw ldapSchemaException; 223 } 224 else 225 { 226 byName.put( lowerName, schemaObject ); 227 } 228 } 229 230 // And register the oid -> schemaObject relation 231 oidRegistry.register( schemaObject ); 232 233 if ( LOG.isDebugEnabled() ) 234 { 235 LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid ); 236 } 237 } 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 public T unregister( String numericOid ) throws LdapException 244 { 245 if ( !Oid.isOid( numericOid ) ) 246 { 247 String msg = I18n.err( I18n.ERR_04272, numericOid ); 248 LOG.error( msg ); 249 throw new LdapException( msg ); 250 } 251 252 T schemaObject = byName.remove( numericOid ); 253 254 for ( String name : schemaObject.getNames() ) 255 { 256 byName.remove( name ); 257 } 258 259 // And remove the SchemaObject from the oidRegistry 260 oidRegistry.unregister( numericOid ); 261 262 if ( DEBUG ) 263 { 264 LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid ); 265 } 266 267 return schemaObject; 268 } 269 270 271 /** 272 * {@inheritDoc} 273 */ 274 public T unregister( T schemaObject ) throws LdapException 275 { 276 String oid = schemaObject.getOid(); 277 278 if ( !byName.containsKey( oid ) ) 279 { 280 String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid ); 281 LOG.warn( msg ); 282 throw new LdapException( msg ); 283 } 284 285 // Remove the oid 286 T removed = byName.remove( oid ); 287 288 /* 289 * Remove the aliases/names from the name map along with their toLowerCase 290 * versions of the name. 291 */ 292 for ( String name : schemaObject.getNames() ) 293 { 294 byName.remove( Strings.trim( Strings.toLowerCase( name ) ) ); 295 } 296 297 // And unregister the oid -> schemaObject relation 298 oidRegistry.unregister( oid ); 299 300 return removed; 301 } 302 303 304 /** 305 * {@inheritDoc} 306 */ 307 public void unregisterSchemaElements( String schemaName ) throws LdapException 308 { 309 if ( schemaName == null ) 310 { 311 return; 312 } 313 314 // Loop on all the SchemaObjects stored and remove those associated 315 // with the give schemaName 316 for ( T schemaObject : this ) 317 { 318 if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) ) 319 { 320 String oid = schemaObject.getOid(); 321 SchemaObject removed = unregister( oid ); 322 323 if ( DEBUG ) 324 { 325 LOG.debug( "Removed {} with oid {} from the registry", removed, oid ); 326 } 327 } 328 } 329 } 330 331 332 /** 333 * {@inheritDoc} 334 */ 335 public String getOidByName( String name ) throws LdapException 336 { 337 T schemaObject = byName.get( name ); 338 339 if ( schemaObject == null ) 340 { 341 // last resort before giving up check with lower cased version 342 String lowerCased = Strings.toLowerCase( name ); 343 344 schemaObject = byName.get( lowerCased ); 345 346 // ok this name is not for a schema object in the registry 347 if ( schemaObject == null ) 348 { 349 throw new LdapException( I18n.err( I18n.ERR_04274, name ) ); 350 } 351 } 352 353 // we found the schema object by key on the first lookup attempt 354 return schemaObject.getOid(); 355 } 356 357 358 /** 359 * {@inheritDoc} 360 */ 361 // This will suppress PMD.EmptyCatchBlock warnings in this method 362 @SuppressWarnings("unchecked") 363 public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original ) 364 { 365 // Fill the byName and OidRegistry maps, the type has already be copied 366 for ( String key : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.keySet() ) 367 { 368 // Clone each SchemaObject 369 T value = ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.get( key ); 370 371 if ( value instanceof LoadableSchemaObject ) 372 { 373 // Update the data structure. 374 // Comparators, Normalizers and SyntaxCheckers aren't copied, 375 // they are immutable 376 byName.put( key, value ); 377 378 // Update the OidRegistry 379 oidRegistry.put( value ); 380 } 381 else 382 { 383 T copiedValue = null; 384 385 // Copy the value if it's not already in the oidRegistry 386 if ( oidRegistry.contains( value.getOid() ) ) 387 { 388 try 389 { 390 copiedValue = oidRegistry.getSchemaObject( value.getOid() ); 391 } 392 catch ( LdapException ne ) 393 { 394 // Can't happen 395 } 396 } 397 else 398 { 399 copiedValue = ( T ) value.copy(); 400 } 401 402 // Update the data structure. 403 byName.put( key, copiedValue ); 404 405 // Update the OidRegistry 406 oidRegistry.put( copiedValue ); 407 } 408 } 409 410 return this; 411 } 412 413 414 /** 415 * {@inheritDoc} 416 */ 417 public T get( String oid ) 418 { 419 try 420 { 421 return oidRegistry.getSchemaObject( oid ); 422 } 423 catch ( LdapException ne ) 424 { 425 return null; 426 } 427 } 428 429 430 /** 431 * {@inheritDoc} 432 */ 433 public SchemaObjectType getType() 434 { 435 return schemaObjectType; 436 } 437 438 439 /** 440 * {@inheritDoc} 441 */ 442 public int size() 443 { 444 return oidRegistry.size(); 445 } 446 447 448 /** 449 * @see Object#toString() 450 */ 451 public String toString() 452 { 453 StringBuilder sb = new StringBuilder(); 454 455 sb.append( schemaObjectType ).append( ": " ); 456 boolean isFirst = true; 457 458 for ( String name : byName.keySet() ) 459 { 460 if ( isFirst ) 461 { 462 isFirst = false; 463 } 464 else 465 { 466 sb.append( ", " ); 467 } 468 469 T schemaObject = byName.get( name ); 470 471 sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' ); 472 } 473 474 return sb.toString(); 475 } 476 477 478 /** 479 * {@inheritDoc} 480 */ 481 public void clear() 482 { 483 // Clear all the schemaObjects 484 for ( SchemaObject schemaObject : oidRegistry ) 485 { 486 // Don't clear LoadableSchemaObject 487 if ( !( schemaObject instanceof LoadableSchemaObject ) ) 488 { 489 schemaObject.clear(); 490 } 491 } 492 493 // Remove the byName elements 494 byName.clear(); 495 496 // Clear the OidRegistry 497 oidRegistry.clear(); 498 } 499}