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