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.core.DirectoryService;
24  import org.apache.directory.server.core.integ.CiRunner;
25  import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
26  import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
27  import org.apache.directory.shared.ldap.name.LdapDN;
28  import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescription;
29  import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
30  import static org.junit.Assert.assertEquals;
31  import static org.junit.Assert.assertTrue;
32  import static org.junit.Assert.assertFalse;
33  import static org.junit.Assert.assertNull;
34  import static org.junit.Assert.assertNotNull;
35  import static org.junit.Assert.fail;
36  import org.junit.Test;
37  import org.junit.runner.RunWith;
38  
39  import javax.naming.NamingEnumeration;
40  import javax.naming.NamingException;
41  import javax.naming.directory.Attribute;
42  import javax.naming.directory.Attributes;
43  import javax.naming.directory.BasicAttribute;
44  import javax.naming.directory.BasicAttributes;
45  import javax.naming.directory.DirContext;
46  import javax.naming.directory.ModificationItem;
47  import javax.naming.directory.SearchControls;
48  import javax.naming.directory.SearchResult;
49  
50  import java.util.ArrayList;
51  import java.util.List;
52  
53  
54  /**
55   * An integration test class for testing persistence for various operations 
56   * on the subschemaSubentry with server restarts.
57   *
58   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
59   * @version $Rev$
60   */
61  @RunWith(CiRunner.class)
62  public class SchemaPersistenceIT
63  {
64      private static final String SUBSCHEMA_SUBENTRY = "subschemaSubentry";
65      private static final AttributeTypeDescriptionSchemaParser ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER = new AttributeTypeDescriptionSchemaParser();
66  
67      public static DirectoryService service;
68  
69  
70      /**
71       * Tests to see if an attributeType is persisted when added, then server 
72       * is shutdown, then restarted again.
73       *
74       * @throws Exception on error
75       */
76      @Test
77      public void testAddAttributeTypePersistence() throws Exception
78      {
79          try
80          {
81              enableSchema( "nis" );
82              List<String> descriptions = new ArrayList<String>();
83  
84              // -------------------------------------------------------------------
85              // test successful add with everything
86              // -------------------------------------------------------------------
87  
88              descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 NAME 'type0' " + "OBSOLETE SUP 2.5.4.41 "
89                  + "EQUALITY caseExactIA5Match " + "ORDERING octetStringOrderingMatch "
90                  + "SUBSTR caseExactIA5SubstringsMatch COLLECTIVE " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
91                  + "SINGLE-VALUE USAGE userApplications X-SCHEMA 'nis' )" );
92              descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 NAME ( 'type1' 'altName' ) "
93                  + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 "
94                  + "NO-USER-MODIFICATION USAGE directoryOperation X-SCHEMA 'nis' )" );
95  
96              modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
97  
98              checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
99              checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
100 
101             // sync operation happens anyway on shutdowns but just to make sure we can do it again
102             service.sync();
103 
104             service.shutdown();
105             service.startup();
106 
107             Attributes attrs = new BasicAttributes( "objectClass", "metaSchema", true );
108             attrs.put( "cn", "blah" );
109             getSchemaContext( service ).createSubcontext( "cn=blah", attrs );
110 
111             checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
112             checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
113         }
114         catch ( Exception e )
115         {
116             e.printStackTrace();
117             throw e;
118         }
119     }
120 
121 
122     // -----------------------------------------------------------------------
123     // Private Utility Methods 
124     // -----------------------------------------------------------------------
125 
126     private void modify( int op, List<String> descriptions, String opAttr ) throws Exception
127     {
128         LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
129         Attribute attr = new BasicAttribute( opAttr );
130         
131         for ( String description : descriptions )
132         {
133             attr.add( description );
134         }
135 
136         Attributes mods = new BasicAttributes( true );
137         mods.put( attr );
138 
139         getRootContext( service ).modifyAttributes( dn, op, mods );
140     }
141 
142 
143     private void enableSchema( String schemaName ) throws Exception
144     {
145         // now enable the test schema
146         ModificationItem[] mods = new ModificationItem[1];
147         Attribute attr = new BasicAttribute( "m-disabled", "FALSE" );
148         mods[0] = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
149         getSchemaContext( service ).modifyAttributes( "cn=" + schemaName, mods );
150     }
151 
152 
153     /**
154      * Get's the subschemaSubentry attribute value from the rootDSE.
155      * 
156      * @return the subschemaSubentry distinguished name
157      * @throws NamingException if there are problems accessing the RootDSE
158      */
159     private String getSubschemaSubentryDN() throws Exception
160     {
161         SearchControls controls = new SearchControls();
162         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
163         controls.setReturningAttributes( new String[]
164             { SUBSCHEMA_SUBENTRY } );
165 
166         NamingEnumeration<SearchResult> results = getRootContext( service ).search( "", "(objectClass=*)", controls );
167         SearchResult result = results.next();
168         results.close();
169         Attribute subschemaSubentry = result.getAttributes().get( SUBSCHEMA_SUBENTRY );
170         return ( String ) subschemaSubentry.get();
171     }
172 
173 
174     /**
175      * Gets the subschemaSubentry attributes for the global schema.
176      * 
177      * @return all operational attributes of the subschemaSubentry 
178      * @throws NamingException if there are problems accessing this entry
179      */
180     private Attributes getSubschemaSubentryAttributes() throws Exception
181     {
182         SearchControls controls = new SearchControls();
183         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
184         controls.setReturningAttributes( new String[]
185             { "+", "*" } );
186 
187         NamingEnumeration<SearchResult> results = getRootContext( service ).search( getSubschemaSubentryDN(),
188             "(objectClass=*)", controls );
189         SearchResult result = results.next();
190         results.close();
191         return result.getAttributes();
192     }
193 
194 
195     private void checkAttributeTypePresent( String oid, String schemaName, boolean isPresent ) throws Exception
196     {
197         // -------------------------------------------------------------------
198         // check first to see if it is present in the subschemaSubentry
199         // -------------------------------------------------------------------
200 
201         Attributes attrs = getSubschemaSubentryAttributes();
202         Attribute attrTypes = attrs.get( "attributeTypes" );
203         AttributeTypeDescription attributeTypeDescription = null;
204         for ( int ii = 0; ii < attrTypes.size(); ii++ )
205         {
206             String desc = ( String ) attrTypes.get( ii );
207             if ( desc.indexOf( oid ) != -1 )
208             {
209                 attributeTypeDescription = ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER
210                     .parseAttributeTypeDescription( desc );
211                 break;
212             }
213         }
214 
215         if ( isPresent )
216         {
217             assertNotNull( attributeTypeDescription );
218             assertEquals( oid, attributeTypeDescription.getNumericOid() );
219         }
220         else
221         {
222             assertNull( attributeTypeDescription );
223         }
224 
225         // -------------------------------------------------------------------
226         // check next to see if it is present in the schema partition
227         // -------------------------------------------------------------------
228 
229         attrs = null;
230 
231         if ( isPresent )
232         {
233             attrs = getSchemaContext( service ).getAttributes( "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
234             assertNotNull( attrs );
235         }
236         else
237         {
238             //noinspection EmptyCatchBlock
239             try
240             {
241                 attrs = getSchemaContext( service ).getAttributes(
242                     "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
243                 fail( "should never get here" );
244             }
245             catch ( NamingException e )
246             {
247             }
248             assertNull( attrs );
249         }
250 
251         // -------------------------------------------------------------------
252         // check to see if it is present in the attributeTypeRegistry
253         // -------------------------------------------------------------------
254 
255         if ( isPresent )
256         {
257             assertTrue( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
258         }
259         else
260         {
261             assertFalse( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
262         }
263     }
264 }