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  package org.apache.directory.server.core.changelog;
20  
21  
22  import org.apache.directory.server.core.DirectoryService;
23  import org.apache.directory.server.core.integ.CiRunner;
24  import static org.apache.directory.server.core.integ.state.TestServiceContext.shutdown;
25  import static org.apache.directory.server.core.integ.state.TestServiceContext.startup;
26  import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
27  import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
28  import static org.junit.Assert.assertTrue;
29  import static org.junit.Assert.assertNull;
30  import static org.junit.Assert.assertNotNull;
31  import static org.junit.Assert.assertEquals;
32  import static org.junit.Assert.fail;
33  import org.junit.Test;
34  import org.junit.runner.RunWith;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  import javax.naming.NamingException;
39  import javax.naming.directory.Attribute;
40  import javax.naming.directory.Attributes;
41  import javax.naming.directory.BasicAttribute;
42  import javax.naming.directory.BasicAttributes;
43  import javax.naming.directory.DirContext;
44  import javax.naming.directory.ModificationItem;
45  import javax.naming.ldap.LdapContext;
46  import java.util.Arrays;
47  
48  
49  /**
50   * Used to test the default change log implementation with an in memory
51   * change log store.  Note that this will probably be removed since this
52   * functionality will be used and tested anyway in all other test cases.
53   *
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev$, $Date$
56   */
57  @RunWith ( CiRunner.class )
58  public class DefaultChangeLogIT
59  {
60      public static final Logger LOG = LoggerFactory.getLogger( DefaultChangeLogIT.class );
61  
62      public static DirectoryService service;
63  
64  
65  //    service.setShutdownHookEnabled( false );
66  
67      @Test
68      public void testManyTagsPersistenceAcrossRestarts() throws Exception, InterruptedException
69      {
70          LdapContext sysRoot = getSystemContext( service );
71          long revision = service.getChangeLog().getCurrentRevision();
72  
73          // add new test entry
74          Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
75          attrs.put( "ou", "test0" );
76          sysRoot.createSubcontext( "ou=test0", attrs );
77          assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );
78  
79          Tag t0 = service.getChangeLog().tag();
80          assertEquals( t0, service.getChangeLog().getLatest() );
81          assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );
82          assertEquals( revision + 1, t0.getRevision() );
83  
84          // add another test entry
85          attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
86          attrs.put( "ou", "test1" );
87          sysRoot.createSubcontext( "ou=test1", attrs );
88          assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );
89  
90          Tag t1 = service.getChangeLog().tag();
91          assertEquals( t1, service.getChangeLog().getLatest() );
92          assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );
93          assertEquals( revision + 2, t1.getRevision() );
94  
95          shutdown();
96          startup();
97  
98          assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );
99          assertEquals( t1, service.getChangeLog().getLatest() );
100         assertEquals( revision + 2, t1.getRevision() );
101 
102         // add third test entry
103         attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
104         attrs.put( "ou", "test2" );
105         sysRoot.createSubcontext( "ou=test2", attrs );
106         assertEquals( revision + 3, service.getChangeLog().getCurrentRevision() );
107 
108         service.revert();
109         sysRoot.getAttributes( "ou=test0" ); // test present
110         sysRoot.getAttributes( "ou=test1" ); // test present
111         assertNotPresent( sysRoot, "ou=test2" );
112         assertEquals( revision + 4, service.getChangeLog().getCurrentRevision() );
113         assertEquals( t1, service.getChangeLog().getLatest() );
114 
115         service.revert( t0.getRevision() );
116         sysRoot.getAttributes( "ou=test0" ); // test present
117         assertNotPresent( sysRoot, "ou=test1" );
118         assertNotPresent( sysRoot, "ou=test2" );
119         assertEquals( revision + 7, service.getChangeLog().getCurrentRevision() );
120         assertEquals( t1, service.getChangeLog().getLatest() );
121 
122         // no sync this time but should happen automatically
123         shutdown();
124         startup();
125         assertEquals( revision + 7, service.getChangeLog().getCurrentRevision() );
126         assertEquals( t1, service.getChangeLog().getLatest() );
127         assertEquals( revision + 2, t1.getRevision() );
128 
129         service.revert( revision );
130         assertNotPresent( sysRoot, "ou=test0" );
131         assertNotPresent( sysRoot, "ou=test1" );
132         assertNotPresent( sysRoot, "ou=test2" );
133         assertEquals( revision + 14, service.getChangeLog().getCurrentRevision() );
134         assertEquals( t1, service.getChangeLog().getLatest() );
135     }
136 
137 
138     @Test
139     public void testTagPersistenceAcrossRestarts() throws Exception, InterruptedException
140     {
141         LdapContext sysRoot = getSystemContext( service );
142         long revision = service.getChangeLog().getCurrentRevision();
143 
144         Tag t0 = service.getChangeLog().tag();
145         assertEquals( t0, service.getChangeLog().getLatest() );
146         assertEquals( revision, service.getChangeLog().getCurrentRevision() );
147 
148         // add new test entry
149         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
150         attrs.put( "ou", "test" );
151         sysRoot.createSubcontext( "ou=test", attrs );
152         assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );
153 
154         shutdown();
155         startup();
156 
157         assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );
158         assertEquals( t0, service.getChangeLog().getLatest() );
159 
160         service.revert();
161         assertNotPresent( sysRoot, "ou=test" );
162         assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );
163         assertEquals( t0, service.getChangeLog().getLatest() );
164     }
165 
166 
167     @Test
168     public void testRevertAddOperations() throws Exception
169     {
170         LdapContext sysRoot = getSystemContext( service );
171         Tag t0 = service.getChangeLog().tag();
172         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
173         attrs.put( "ou", "test" );
174         sysRoot.createSubcontext( "ou=test", attrs );
175 
176         assertNotNull( sysRoot.getAttributes( "ou=test" ) );
177         service.revert( t0.getRevision() );
178 
179         try
180         {
181             sysRoot.getAttributes( "ou=test" );
182             fail( "Should not be able to find the entry!" );
183         }
184         catch ( NamingException ne )
185         {
186             assertTrue( ne instanceof LdapNameNotFoundException );
187         }
188     }
189 
190 
191     @Test
192     public void testRevertAddAndDeleteOperations() throws Exception
193     {
194         LdapContext sysRoot = getSystemContext( service );
195         Tag t0 = service.getChangeLog().tag();
196 
197         // add new test entry
198         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
199         attrs.put( "ou", "test" );
200         sysRoot.createSubcontext( "ou=test", attrs );
201 
202         // assert presence
203         assertNotNull( sysRoot.getAttributes( "ou=test" ) );
204 
205         // delete the test entry and test that it is gone
206         sysRoot.destroySubcontext( "ou=test" );
207         assertNotPresent( sysRoot, "ou=test" );
208 
209         // now revert back to begining the added entry is still gone
210         service.revert( t0.getRevision() );
211         assertNotPresent( sysRoot, "ou=test" );
212     }
213 
214 
215     @Test
216     public void testRevertDeleteOperations() throws Exception
217     {
218         LdapContext sysRoot = getSystemContext( service );
219         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
220         attrs.put( "ou", "test" );
221         sysRoot.createSubcontext( "ou=test", attrs );
222 
223         // tag after the addition before deletion
224         Tag t0 = service.getChangeLog().tag();
225         assertNotNull( sysRoot.getAttributes( "ou=test" ) );
226 
227         // delete the test entry and test that it is gone
228         sysRoot.destroySubcontext( "ou=test" );
229         assertNotPresent( sysRoot, "ou=test" );
230 
231         // now revert and assert that the added entry re-appears
232         service.revert( t0.getRevision() );
233         assertNotNull( sysRoot.getAttributes( "ou=test" ) );
234     }
235 
236 
237     @Test
238     public void testRevertRenameOperations() throws Exception
239     {
240         LdapContext sysRoot = getSystemContext( service );
241         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
242         attrs.put( "ou", "oldname" );
243         sysRoot.createSubcontext( "ou=oldname", attrs );
244 
245         // tag after the addition before rename
246         Tag t0 = service.getChangeLog().tag();
247         assertNotNull( sysRoot.getAttributes( "ou=oldname" ) );
248 
249         // rename the test entry and test that the rename occurred
250         sysRoot.rename( "ou=oldname", "ou=newname" );
251         assertNotPresent( sysRoot, "ou=oldname" );
252         assertNotNull( sysRoot.getAttributes( "ou=newname" ) );
253 
254         // now revert and assert that the rename was reversed
255         service.revert( t0.getRevision() );
256         assertNotPresent( sysRoot, "ou=newname" );
257         assertNotNull( sysRoot.getAttributes( "ou=oldname" ) );
258     }
259 
260 
261     @Test
262     public void testRevertModifyOperations() throws Exception
263     {
264         LdapContext sysRoot = getSystemContext( service );
265         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
266         attrs.put( "ou", "test5" );
267         sysRoot.createSubcontext( "ou=test5", attrs );
268 
269         // -------------------------------------------------------------------
270         // Modify ADD Test
271         // -------------------------------------------------------------------
272 
273         // tag after the addition before modify ADD
274         Tag t0 = service.getChangeLog().tag();
275         assertNotNull( sysRoot.getAttributes( "ou=test5" ) );
276 
277         // modify the test entry to add description and test new attr appears
278         sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,
279                 new BasicAttributes( "description", "a desc value", true ) );
280         Attributes resusitated = sysRoot.getAttributes( "ou=test5" );
281         assertNotNull( resusitated );
282         Attribute description = resusitated.get( "description" );
283         assertNotNull( description );
284         assertEquals( "a desc value", description.get() );
285 
286         // now revert and assert that the added entry re-appears
287         service.revert( t0.getRevision() );
288         resusitated = sysRoot.getAttributes( "ou=test5" );
289         assertNotNull( resusitated );
290         assertNull( resusitated.get( "description" ) );
291 
292         // -------------------------------------------------------------------
293         // Modify REPLACE Test
294         // -------------------------------------------------------------------
295 
296         // add the attribute again and make sure it is old value
297         sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,
298                 new BasicAttributes( "description", "old value", true ) );
299         resusitated = sysRoot.getAttributes( "ou=test5" );
300         assertNotNull( resusitated );
301         description = resusitated.get( "description" );
302         assertNotNull( description );
303         assertEquals( description.get(), "old value" );
304 
305         // now tag then replace the value to "new value" and confirm
306         Tag t1 = service.getChangeLog().tag();
307         sysRoot.modifyAttributes( "ou=test5", DirContext.REPLACE_ATTRIBUTE,
308                 new BasicAttributes( "description", "new value", true ) );
309         resusitated = sysRoot.getAttributes( "ou=test5" );
310         assertNotNull( resusitated );
311         description = resusitated.get( "description" );
312         assertNotNull( description );
313         assertEquals( description.get(), "new value" );
314 
315         // now revert and assert the old value is now reverted
316         service.revert( t1.getRevision() );
317         resusitated = sysRoot.getAttributes( "ou=test5" );
318         assertNotNull( resusitated );
319         description = resusitated.get( "description" );
320         assertNotNull( description );
321         assertEquals( description.get(), "old value" );
322 
323 
324         // -------------------------------------------------------------------
325         // Modify REMOVE Test
326         // -------------------------------------------------------------------
327 
328         Tag t2 = service.getChangeLog().tag();
329         sysRoot.modifyAttributes( "ou=test5", DirContext.REMOVE_ATTRIBUTE,
330                 new BasicAttributes( "description", "old value", true ) );
331         resusitated = sysRoot.getAttributes( "ou=test5" );
332         assertNotNull( resusitated );
333         description = resusitated.get( "description" );
334         assertNull( description );
335 
336         // now revert and assert the old value is now reverted
337         service.revert( t2.getRevision() );
338         resusitated = sysRoot.getAttributes( "ou=test5" );
339         assertNotNull( resusitated );
340         description = resusitated.get( "description" );
341         assertNotNull( description );
342         assertEquals( description.get(), "old value" );
343 
344         // -------------------------------------------------------------------
345         // Modify Multi Operation Test
346         // -------------------------------------------------------------------
347 
348         // add a userPassword attribute so we can test replacing it
349         sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,
350                 new BasicAttributes( "userPassword", "to be replaced", true ) );
351         assertPassword( sysRoot.getAttributes( "ou=test5" ), "to be replaced" );
352 
353         ModificationItem[] mods = new ModificationItem[]
354         {
355             new ModificationItem( DirContext.REMOVE_ATTRIBUTE,
356                     new BasicAttribute( "description", "old value" ) ),
357             new ModificationItem( DirContext.ADD_ATTRIBUTE,
358                     new BasicAttribute( "seeAlso", "ou=added" ) ),
359             new ModificationItem( DirContext.REPLACE_ATTRIBUTE,
360                     new BasicAttribute( "userPassword", "a replaced value" ) )
361         };
362         Tag t3 = service.getChangeLog().tag();
363 
364         // now make the modification and check that description is gone,
365         // seeAlso is added, and that the userPassword has been replaced
366         sysRoot.modifyAttributes( "ou=test5", mods );
367         resusitated = sysRoot.getAttributes( "ou=test5" );
368         assertNotNull( resusitated );
369         description = resusitated.get( "description" );
370         assertNull( description );
371         assertPassword( resusitated, "a replaced value" );
372         Attribute seeAlso = resusitated.get( "seeAlso" );
373         assertNotNull( seeAlso );
374         assertEquals( seeAlso.get(), "ou=added" );
375 
376         // now we revert and make sure the old values are as they were
377         service.revert( t3.getRevision() );
378         resusitated = sysRoot.getAttributes( "ou=test5" );
379         assertNotNull( resusitated );
380         description = resusitated.get( "description" );
381         assertNotNull( description );
382         assertEquals( description.get(), "old value" );
383         assertPassword( resusitated, "to be replaced" );
384         seeAlso = resusitated.get( "seeAlso" );
385         assertNull( seeAlso );
386     }
387 
388 
389     private void assertPassword( Attributes entry, String password ) throws NamingException
390     {
391         Attribute userPassword = entry.get( "userPassword" );
392         assertNotNull( userPassword );
393         Arrays.equals( password.getBytes(), ( byte[] ) userPassword.get() );
394     }
395 
396 
397     private void assertNotPresent( DirContext ctx, String dn ) throws NamingException
398     {
399         try
400         {
401             ctx.getAttributes( dn );
402             fail( "Should not be able to find the entry " + dn + " but it is still there." );
403         }
404         catch ( NamingException ne )
405         {
406             assertTrue( ne instanceof LdapNameNotFoundException );
407         }
408     }
409 }