1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.schema;
21  
22  
23  import jdbm.helper.IntegerComparator;
24  import jdbm.helper.StringComparator;
25  import org.apache.directory.server.constants.MetaSchemaConstants;
26  import org.apache.directory.server.core.DirectoryService;
27  import org.apache.directory.server.core.integ.CiRunner;
28  import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
29  import org.apache.directory.server.schema.registries.ComparatorRegistry;
30  import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
31  import org.apache.directory.server.schema.registries.OidRegistry;
32  import org.apache.directory.shared.ldap.constants.SchemaConstants;
33  import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
34  import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
35  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
36  import org.apache.directory.shared.ldap.name.LdapDN;
37  import org.apache.directory.shared.ldap.schema.MatchingRule;
38  import org.apache.directory.shared.ldap.schema.Normalizer;
39  import org.apache.directory.shared.ldap.schema.Syntax;
40  import static org.junit.Assert.assertEquals;
41  import static org.junit.Assert.assertTrue;
42  import static org.junit.Assert.assertFalse;
43  import static org.junit.Assert.fail;
44  import org.junit.Test;
45  import org.junit.runner.RunWith;
46  
47  import javax.naming.NamingException;
48  import javax.naming.directory.Attribute;
49  import javax.naming.directory.Attributes;
50  import javax.naming.directory.BasicAttribute;
51  import javax.naming.directory.BasicAttributes;
52  import javax.naming.directory.DirContext;
53  import javax.naming.directory.ModificationItem;
54  
55  import java.io.ByteArrayOutputStream;
56  import java.io.InputStream;
57  import java.util.Comparator;
58  
59  
60  /**
61   * A test case which tests the addition of various schema elements
62   * to the ldap server.
63   *
64   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
65   * @version $Rev$
66   */
67  @RunWith ( CiRunner.class )
68  public class MetaComparatorHandlerIT
69  {
70      private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.100000";
71      private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.1.100001";
72  
73  
74      public static DirectoryService service;
75  
76      
77      /**
78       * Gets relative DN to ou=schema.
79       *
80       * @param schemaName the name of the schema
81       * @return the dn to the ou underwhich comparators are found for a schmea
82       * @throws Exception if there are dn construction issues
83       */
84      private LdapDN getComparatorContainer( String schemaName ) throws Exception
85      {
86          return new LdapDN( "ou=comparators,cn=" + schemaName );
87      }
88  
89  
90      private static ComparatorRegistry getComparatorRegistry()
91      {
92          return service.getRegistries().getComparatorRegistry();
93      }
94      
95  
96      private static MatchingRuleRegistry getMatchingRuleRegistry()
97      {
98          return service.getRegistries().getMatchingRuleRegistry();
99      }
100 
101 
102     private static OidRegistry getOidRegistry()
103     {
104         return service.getRegistries().getOidRegistry();
105     }
106 
107 
108     // ----------------------------------------------------------------------
109     // Test all core methods with normal operational pathways
110     // ----------------------------------------------------------------------
111 
112 
113     @Test
114     public void testAddComparator() throws Exception
115     {
116         Attributes attrs = new BasicAttributes( true );
117         Attribute oc = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT, "top" );
118         oc.add( MetaSchemaConstants.META_TOP_OC );
119         oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
120         attrs.put( oc );
121         attrs.put( MetaSchemaConstants.M_FQCN_AT, StringComparator.class.getName() );
122         attrs.put( MetaSchemaConstants.M_OID_AT, OID );
123         attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
124         
125         LdapDN dn = getComparatorContainer( "apachemeta" );
126         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
127         getSchemaContext( service ).createSubcontext( dn, attrs );
128         
129         assertTrue( getComparatorRegistry().hasComparator( OID ) );
130         assertEquals( getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
131         Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
132         assertEquals( clazz, StringComparator.class );
133     }
134     
135 
136     @Test
137     public void testAddComparatorWithByteCode() throws Exception
138     {
139         InputStream in = getClass().getResourceAsStream( "DummyComparator.bytecode" );
140         ByteArrayOutputStream out = new ByteArrayOutputStream();
141         while ( in.available() > 0 )
142         {
143             out.write( in.read() );
144         }
145         
146         Attributes attrs = new BasicAttributes( true );
147         Attribute oc = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT, "top" );
148         oc.add( MetaSchemaConstants.META_TOP_OC );
149         oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
150         attrs.put( oc );
151         attrs.put( MetaSchemaConstants.M_FQCN_AT, "DummyComparator" );
152         attrs.put( MetaSchemaConstants.M_BYTECODE_AT, out.toByteArray() );
153         attrs.put( MetaSchemaConstants.M_OID_AT, OID );
154         attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
155         
156         LdapDN dn = getComparatorContainer( "apachemeta" );
157         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
158         getSchemaContext( service ).createSubcontext( dn, attrs );
159         
160         assertTrue( getComparatorRegistry().hasComparator( OID ) );
161         assertEquals( getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
162         Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
163         assertEquals( clazz.getName(), "DummyComparator" );
164     }
165     
166 
167     @Test
168     public void testDeleteComparator() throws Exception
169     {
170         LdapDN dn = getComparatorContainer( "apachemeta" );
171         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
172         testAddComparator();
173         
174         getSchemaContext( service ).destroySubcontext( dn );
175 
176         assertFalse( "comparator should be removed from the registry after being deleted", 
177             getComparatorRegistry().hasComparator( OID ) );
178         
179         try
180         {
181             getComparatorRegistry().lookup( OID );
182             fail( "comparator lookup should fail after deleting the comparator" );
183         }
184         catch( NamingException e )
185         {
186         }
187     }
188 
189 
190     @Test
191     public void testRenameComparator() throws Exception
192     {
193         LdapDN dn = getComparatorContainer( "apachemeta" );
194         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
195         testAddComparator();
196         
197         LdapDN newdn = getComparatorContainer( "apachemeta" );
198         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
199         getSchemaContext( service ).rename( dn, newdn );
200 
201         assertFalse( "old comparator OID should be removed from the registry after being renamed", 
202             getComparatorRegistry().hasComparator( OID ) );
203         
204         try
205         {
206             getComparatorRegistry().lookup( OID );
207             fail( "comparator lookup should fail after deleting the comparator" );
208         }
209         catch( NamingException e )
210         {
211         }
212 
213         assertTrue( getComparatorRegistry().hasComparator( NEW_OID ) );
214         Class<?> clazz = getComparatorRegistry().lookup( NEW_OID ).getClass();
215         assertEquals( clazz, StringComparator.class );
216     }
217 
218 
219     @Test
220     public void testMoveComparator() throws Exception
221     {
222         testAddComparator();
223         
224         LdapDN dn = getComparatorContainer( "apachemeta" );
225         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
226 
227         LdapDN newdn = getComparatorContainer( "apache" );
228         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
229         
230         getSchemaContext( service ).rename( dn, newdn );
231 
232         assertTrue( "comparator OID should still be present", 
233             getComparatorRegistry().hasComparator( OID ) );
234         
235         assertEquals( "comparator schema should be set to apache not apachemeta", 
236             getComparatorRegistry().getSchemaName( OID ), "apache" );
237 
238         Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
239         assertEquals( clazz, StringComparator.class );
240     }
241 
242 
243     @Test
244     public void testMoveComparatorAndChangeRdn() throws Exception
245     {
246         testAddComparator();
247         
248         LdapDN dn = getComparatorContainer( "apachemeta" );
249         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
250 
251         LdapDN newdn = getComparatorContainer( "apache" );
252         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
253         
254         getSchemaContext( service ).rename( dn, newdn );
255 
256         assertFalse( "old comparator OID should NOT be present", 
257             getComparatorRegistry().hasComparator( OID ) );
258         
259         assertTrue( "new comparator OID should be present", 
260             getComparatorRegistry().hasComparator( NEW_OID ) );
261         
262         assertEquals( "comparator with new oid should have schema set to apache NOT apachemeta", 
263             getComparatorRegistry().getSchemaName( NEW_OID ), "apache" );
264 
265         Class<?> clazz = getComparatorRegistry().lookup( NEW_OID ).getClass();
266         assertEquals( clazz, StringComparator.class );
267     }
268 
269 
270     @Test
271     public void testModifyComparatorWithModificationItems() throws Exception
272     {
273         testAddComparator();
274         
275         LdapDN dn = getComparatorContainer( "apachemeta" );
276         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
277         
278         ModificationItem[] mods = new ModificationItem[1];
279         Attribute attr = new BasicAttribute( MetaSchemaConstants.M_FQCN_AT, IntegerComparator.class.getName() );
280         mods[0] = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
281         getSchemaContext( service ).modifyAttributes( dn, mods );
282 
283         assertTrue( "comparator OID should still be present", 
284             getComparatorRegistry().hasComparator( OID ) );
285         
286         assertEquals( "comparator schema should be set to apachemeta", 
287             getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
288 
289         Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
290         assertEquals( clazz, IntegerComparator.class );
291     }
292 
293 
294     @Test
295     public void testModifyComparatorWithAttributes() throws Exception
296     {
297         testAddComparator();
298         
299         LdapDN dn = getComparatorContainer( "apachemeta" );
300         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
301         
302         Attributes mods = new BasicAttributes( true );
303         mods.put( MetaSchemaConstants.M_FQCN_AT, IntegerComparator.class.getName() );
304         getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
305 
306         assertTrue( "comparator OID should still be present", 
307             getComparatorRegistry().hasComparator( OID ) );
308         
309         assertEquals( "comparator schema should be set to apachemeta", 
310             getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
311 
312         Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
313         assertEquals( clazz, IntegerComparator.class );
314     }
315     
316 
317     // ----------------------------------------------------------------------
318     // Test move, rename, and delete when a MR exists and uses the Comparator
319     // ----------------------------------------------------------------------
320 
321     
322     @Test
323     public void testDeleteComparatorWhenInUse() throws Exception
324     {
325         LdapDN dn = getComparatorContainer( "apachemeta" );
326         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
327         testAddComparator();
328         getMatchingRuleRegistry().register( new DummyMR() );
329         
330         try
331         {
332             getSchemaContext( service ).destroySubcontext( dn );
333             fail( "should not be able to delete a comparator in use" );
334         }
335         catch( LdapOperationNotSupportedException e ) 
336         {
337             assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
338         }
339 
340         assertTrue( "comparator should still be in the registry after delete failure", 
341             getComparatorRegistry().hasComparator( OID ) );
342         getMatchingRuleRegistry().unregister( OID );
343         getOidRegistry().unregister( OID );
344     }
345     
346     
347     @Test
348     public void testMoveComparatorWhenInUse() throws Exception
349     {
350         testAddComparator();
351         getMatchingRuleRegistry().register( new DummyMR() );
352         
353         LdapDN dn = getComparatorContainer( "apachemeta" );
354         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
355 
356         LdapDN newdn = getComparatorContainer( "apache" );
357         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
358         
359         try
360         {
361             getSchemaContext( service ).rename( dn, newdn );
362             fail( "should not be able to move a comparator in use" );
363         }
364         catch( LdapOperationNotSupportedException e ) 
365         {
366             assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
367         }
368 
369         assertTrue( "comparator should still be in the registry after move failure", 
370             getComparatorRegistry().hasComparator( OID ) );
371         getMatchingRuleRegistry().unregister( OID );
372         getOidRegistry().unregister( OID );
373     }
374 
375 
376     @Test
377     public void testMoveComparatorAndChangeRdnWhenInUse() throws Exception
378     {
379         testAddComparator();
380         getMatchingRuleRegistry().register( new DummyMR() );
381         
382         LdapDN dn = getComparatorContainer( "apachemeta" );
383         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
384 
385         LdapDN newdn = getComparatorContainer( "apache" );
386         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
387         
388         try
389         {
390             getSchemaContext( service ).rename( dn, newdn );
391             fail( "should not be able to move a comparator in use" );
392         }
393         catch( LdapOperationNotSupportedException e ) 
394         {
395             assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
396         }
397 
398         assertTrue( "comparator should still be in the registry after move failure", 
399             getComparatorRegistry().hasComparator( OID ) );
400         getMatchingRuleRegistry().unregister( OID );
401         getOidRegistry().unregister( OID );
402     }
403 
404     
405     @Test
406     public void testRenameComparatorWhenInUse() throws Exception
407     {
408         LdapDN dn = getComparatorContainer( "apachemeta" );
409         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
410         testAddComparator();
411         getMatchingRuleRegistry().register( new DummyMR() );
412         
413         LdapDN newdn = getComparatorContainer( "apachemeta" );
414         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
415         
416         try
417         {
418             getSchemaContext( service ).rename( dn, newdn );
419             fail( "should not be able to rename a comparator in use" );
420         }
421         catch( LdapOperationNotSupportedException e ) 
422         {
423             assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
424         }
425 
426         assertTrue( "comparator should still be in the registry after rename failure", 
427             getComparatorRegistry().hasComparator( OID ) );
428         getMatchingRuleRegistry().unregister( OID );
429         getOidRegistry().unregister( OID );
430     }
431 
432 
433     // ----------------------------------------------------------------------
434     // Let's try some freaky stuff
435     // ----------------------------------------------------------------------
436 
437 
438     @Test
439     public void testMoveComparatorToTop() throws Exception
440     {
441         testAddComparator();
442         
443         LdapDN dn = getComparatorContainer( "apachemeta" );
444         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
445 
446         LdapDN top = new LdapDN();
447         top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
448         
449         try
450         {
451             getSchemaContext( service ).rename( dn, top );
452             fail( "should not be able to move a comparator up to ou=schema" );
453         }
454         catch( LdapInvalidNameException e ) 
455         {
456             assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
457         }
458 
459         assertTrue( "comparator should still be in the registry after move failure", 
460             getComparatorRegistry().hasComparator( OID ) );
461     }
462 
463 
464     @Test
465     public void testMoveComparatorToNormalizers() throws Exception
466     {
467         testAddComparator();
468         
469         LdapDN dn = getComparatorContainer( "apachemeta" );
470         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
471 
472         LdapDN newdn = new LdapDN( "ou=normalizers,cn=apachemeta" );
473         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
474         
475         try
476         {
477             getSchemaContext( service ).rename( dn, newdn );
478             fail( "should not be able to move a comparator up to normalizers container" );
479         }
480         catch( LdapInvalidNameException e ) 
481         {
482             assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
483         }
484 
485         assertTrue( "comparator should still be in the registry after move failure", 
486             getComparatorRegistry().hasComparator( OID ) );
487     }
488     
489     
490     @Test
491     public void testAddComparatorToDisabledSchema() throws Exception
492     {
493         Attributes attrs = new BasicAttributes( true );
494         Attribute oc = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT, "top" );
495         oc.add( MetaSchemaConstants.META_TOP_OC );
496         oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
497         attrs.put( oc );
498         attrs.put( MetaSchemaConstants.M_FQCN_AT, StringComparator.class.getName() );
499         attrs.put( MetaSchemaConstants.M_OID_AT, OID );
500         attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
501         
502         // nis is by default inactive
503         LdapDN dn = getComparatorContainer( "nis" );
504         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
505         getSchemaContext( service ).createSubcontext( dn, attrs );
506         
507         assertFalse( "adding new comparator to disabled schema should not register it into the registries", 
508             getComparatorRegistry().hasComparator( OID ) );
509     }
510 
511 
512     @Test
513     public void testMoveComparatorToDisabledSchema() throws Exception
514     {
515         testAddComparator();
516         
517         LdapDN dn = getComparatorContainer( "apachemeta" );
518         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
519 
520         // nis is inactive by default
521         LdapDN newdn = getComparatorContainer( "nis" );
522         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
523         
524         getSchemaContext( service ).rename( dn, newdn );
525 
526         assertFalse( "comparator OID should no longer be present", 
527             getComparatorRegistry().hasComparator( OID ) );
528     }
529 
530 
531     @Test
532     public void testMoveComparatorToEnabledSchema() throws Exception
533     {
534         testAddComparatorToDisabledSchema();
535         
536         // nis is inactive by default
537         LdapDN dn = getComparatorContainer( "nis" );
538         dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
539 
540         assertFalse( "comparator OID should NOT be present when added to disabled nis schema", 
541             getComparatorRegistry().hasComparator( OID ) );
542 
543         LdapDN newdn = getComparatorContainer( "apachemeta" );
544         newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
545         
546         getSchemaContext( service ).rename( dn, newdn );
547 
548         assertTrue( "comparator OID should be present when moved to enabled schema", 
549             getComparatorRegistry().hasComparator( OID ) );
550         
551         assertEquals( "comparator should be in apachemeta schema after move", 
552             getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
553     }
554 
555 
556     class DummyMR implements MatchingRule
557     {
558         private static final long serialVersionUID = 1L;
559 
560         public Comparator getComparator() throws NamingException
561         {
562             return null;
563         }
564 
565         public Normalizer getNormalizer() throws NamingException
566         {
567             return null;
568         }
569 
570         public Syntax getSyntax() throws NamingException
571         {
572             return null;
573         }
574 
575         public String getDescription()
576         {
577             return null;
578         }
579 
580         public String getName()
581         {
582             return "dummy";
583         }
584 
585         public String[] getNames()
586         {
587             return new String[] { "dummy" };
588         }
589 
590         public String getOid()
591         {
592             return OID;
593         }
594 
595         public boolean isObsolete()
596         {
597             return false;
598         }
599 
600         public String getSchema()
601         {
602             return null;
603         }
604 
605         public void setSchema( String schemaName )
606         {
607         }
608 
609         public String[] getNamesRef()
610         {
611             return new String[] { "dummy" };
612         }
613     }
614 }