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.schema.registries;
021
022
023 import java.util.ArrayList;
024 import java.util.Collections;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.concurrent.ConcurrentHashMap;
029
030 import javax.naming.NamingException;
031 import javax.naming.directory.NoSuchAttributeException;
032
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035 import org.apache.directory.shared.asn1.primitives.OID;
036 import org.apache.directory.shared.ldap.util.StringTools;
037
038
039 /**
040 * Default OID registry implementation used to resolve a schema object OID
041 * to a name and vice-versa.
042 * <br/>
043 * We are storing the schema elements in two data structures :
044 * <li>an oid to names map</li>
045 * <li>a name to oid map</li>
046 * <br/>
047 * The first data structure contains a list of names associated with the given
048 * oid. The oid itself is not necessarily stored in this list, unless the schema
049 * object does not have any name.<br/>
050 * The second data structure contains all the names with the associated OID. We
051 * also store the oid -> oid relation, to allow us to look for a registered
052 * schema object using its oid.<br/>
053 * <br/>
054 * For instance, if we have registered the C AttributeType, which OID is
055 * 2.5.4.6 and has another name, 'country', the data structure will contain :<br>
056 *
057 * <ul>
058 * <li>
059 * byOid
060 * <ul>
061 * <li>2.5.4.6 -> {'c', 'country'}</li>
062 * </ul>
063 * </li>
064 * <li>
065 * byName
066 * <ul>
067 * <li>'c' -> 2.5.4.6</li>
068 * <li>'country' -> 2.5.4.6</li>
069 * <li>'2.5.4.6' -> 2.5.4.6</li>
070 * </ul>
071 * </li>
072 * </ul>
073 *
074 *
075 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
076 * @version $Rev: 777089 $
077 */
078 public class DefaultOidRegistry implements OidRegistry
079 {
080 /** static class logger */
081 private static final Logger LOG = LoggerFactory.getLogger( DefaultOidRegistry.class );
082
083 /** Speedup for DEBUG mode */
084 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
085
086 /** Maps OID to a name or a list of names if more than one name exists */
087 private Map<String, List<String>> byOid = new ConcurrentHashMap<String, List<String>>();
088
089 /** Maps several names to an OID */
090 private Map<String,String> byName = new ConcurrentHashMap<String,String>();
091
092
093 /**
094 * {@inheritDoc}
095 */
096 public String getOid( String name ) throws NamingException
097 {
098 if ( StringTools.isEmpty( name ) )
099 {
100 throw new NamingException( "name should not be empty" );
101 }
102
103 /* If name is an OID then we return it back since inherently the
104 * OID is another name for the object referred to by OID and the
105 * caller does not know that the argument is an OID String.
106 */
107 if ( StringTools.isDigit( name.charAt( 0 ) ) )
108 {
109 return name;
110 }
111
112 // If name is mapped to a OID already return OID
113 if ( byName.containsKey( name ) )
114 {
115 String oid = byName.get( name );
116
117 if ( IS_DEBUG )
118 {
119 LOG.debug( "looked up OID '" + oid + "' with id '" + name + "'" );
120 }
121
122 return oid;
123 }
124
125 /*
126 * As a last resort we check if name is not normalized and if the
127 * normalized version used as a key returns an OID. If the normalized
128 * name works add the normalized name as a key with its OID to the
129 * byName lookup. BTW these normalized versions of the key are not
130 * returned on a getNameSet.
131 */
132 String lowerCase = name.trim().toLowerCase();
133
134 String oid = byName.get( lowerCase );
135
136 if ( oid != null )
137 {
138 if ( IS_DEBUG )
139 {
140 LOG.debug( "looked up OID '{}' with id '{}'", oid, name );
141 }
142
143 return oid;
144 }
145
146 NamingException fault = new NoSuchAttributeException( "OID for name '" + name + "' was not "
147 + "found within the OID registry" );
148 LOG.warn( fault.getMessage() );
149 throw fault;
150 }
151
152
153 /**
154 * {@inheritDoc}
155 */
156 public boolean hasOid( String name )
157 {
158 if ( StringTools.isEmpty( name ) )
159 {
160 return false;
161 }
162
163 String normalized = name.trim().toLowerCase();
164
165 return byName.containsKey( normalized );
166 }
167
168
169 /**
170 * {@inheritDoc}
171 */
172 public String getPrimaryName( String oid ) throws NamingException
173 {
174 List<String> value = byOid.get( oid );
175
176 if ( null == value )
177 {
178 String msg = "OID '" + oid + "' was not found within the OID registry";
179 LOG.error( msg );
180 throw new NamingException( msg );
181 }
182
183 String name = value.get( 0 );
184
185 if ( IS_DEBUG )
186 {
187 LOG.debug( "looked up primary name '{}' with OID '{}'", name, oid );
188 }
189
190 return name;
191 }
192
193
194 /**
195 * {@inheritDoc}
196 */
197 public List<String> getNameSet( String oid ) throws NamingException
198 {
199 List<String> value = byOid.get( oid );
200
201 if ( null == value )
202 {
203 String msg = "OID '" + oid + "' was not found within the OID registry";
204 LOG.error( msg );
205 throw new NamingException( msg );
206 }
207
208 if ( IS_DEBUG )
209 {
210 LOG.debug( "looked up names '{}' for OID '{}'", value, oid );
211 }
212
213 return value;
214 }
215
216
217 /**
218 * {@inheritDoc}
219 */
220 public Iterator<String> list()
221 {
222 return Collections.unmodifiableSet( byOid.keySet() ).iterator();
223 }
224
225
226 /**
227 * {@inheritDoc}
228 */
229 public Map<String, String> getOidByName()
230 {
231 return byName;
232 }
233
234
235 /**
236 * {@inheritDoc}
237 */
238 public Map<String, List<String>> getNameByOid()
239 {
240 return byOid;
241 }
242
243
244 /**
245 * {@inheritDoc}
246 */
247 public void register( String name, String oid ) throws NamingException
248 {
249 if ( !OID.isOID( oid ) )
250 {
251 String message = "Swap the parameter order: the oid " +
252 "does not start with a digit, or is not an OID!";
253
254 LOG.debug( message );
255 throw new NamingException( message );
256 }
257
258 if ( StringTools.isEmpty( name ) )
259 {
260 String message = "The name is empty for OID " + oid;
261 LOG.error( message );
262 throw new NamingException( message );
263 }
264
265 /*
266 * Add the entry for the given name as is and its lowercased version if
267 * the lower cased name is different from the given name name.
268 */
269 String lowerCase = name.trim().toLowerCase();
270
271 // Put both the name and the oid as names
272 byName.put( lowerCase, oid );
273 byName.put( oid, oid );
274
275 /*
276 * Update OID Map
277 *
278 * 1). Check if we already have a value[s] stored
279 * 1a). Value is a single value and is a String
280 * Replace value with list containing old and new values
281 * 1b). More than one value stored in a list
282 * Add new value to the list
283 * 2). If we do not have a value then we just add it as a String
284 */
285 List<String> value = byOid.get( oid );
286
287 if ( value == null )
288 {
289 value = new ArrayList<String>( 1 );
290 value.add( lowerCase );
291 }
292 else
293 {
294 if ( value.contains( lowerCase ) )
295 {
296 return;
297 }
298
299 value.add( lowerCase );
300 }
301
302 byOid.put( oid, value );
303
304 if ( IS_DEBUG )
305 {
306 LOG.debug( "registed name '" + name + "' with OID: " + oid );
307 }
308 }
309
310
311 /**
312 * {@inheritDoc}
313 */
314 public void unregister( String numericOid ) throws NamingException
315 {
316 // First, remove the <OID, names> from the byOID map
317 List<String> names = byOid.remove( numericOid );
318
319 // Then remove all the <name, OID> from the byName map
320 if ( names != null )
321 {
322 for ( String name:names )
323 {
324 byName.remove( name );
325 }
326 }
327
328 // Last, remove the <OID, OID> from the byName map
329 byName.remove( numericOid );
330
331 if ( IS_DEBUG )
332 {
333 LOG.debug( "Unregisted name '{}' with OID: {}", StringTools.listToString( names ), numericOid );
334 }
335 }
336 }