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.mitosis.store.derby;
21   
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.UUID;
33  
34  import javax.naming.Name;
35  import javax.naming.ldap.LdapName;
36  
37  import junit.framework.Assert;
38  import junit.framework.TestCase;
39  
40  import org.apache.commons.io.FileUtils;
41  import org.apache.directory.server.core.DefaultDirectoryService;
42  import org.apache.directory.server.core.entry.DefaultServerAttribute;
43  import org.apache.directory.server.core.entry.DefaultServerEntry;
44  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
45  import org.apache.directory.shared.ldap.entry.EntryAttribute;
46  import org.apache.directory.shared.ldap.name.LdapDN;
47  import org.apache.directory.shared.ldap.schema.AttributeType;
48  import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
49  import org.apache.directory.shared.ldap.schema.OidNormalizer;
50  import org.apache.directory.mitosis.common.CSN;
51  import org.apache.directory.mitosis.common.CSNFactory;
52  import org.apache.directory.mitosis.common.CSNVector;
53  import org.apache.directory.mitosis.common.DefaultCSN;
54  import org.apache.directory.mitosis.common.DefaultCSNFactory;
55  import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
56  import org.apache.directory.mitosis.operation.AddAttributeOperation;
57  import org.apache.directory.mitosis.operation.AddEntryOperation;
58  import org.apache.directory.mitosis.operation.CompositeOperation;
59  import org.apache.directory.mitosis.operation.DeleteAttributeOperation;
60  import org.apache.directory.mitosis.operation.Operation;
61  import org.apache.directory.mitosis.operation.ReplaceAttributeOperation;
62  import org.apache.directory.mitosis.store.ReplicationLogIterator;
63  import org.apache.directory.mitosis.store.ReplicationStoreException;
64  
65  
66  public class DerbyReplicationStoreTest extends TestCase
67  {
68      private static final String REPLICA_ID =  "TEST_REPLICA";
69      private static final String OTHER_REPLICA_ID = "OTHER_REPLICA";
70      private static final String OTHER_REPLICA_ID_2 = "OTHER_REPLICA_2";
71      private static final File DB_PATH = new File( "target/testDB" );
72  
73      private final CSNFactory csnFactory = new DefaultCSNFactory();
74      private DerbyReplicationStore store;
75      private int testCount;
76      private long startTime;
77      private DefaultDirectoryService service;
78  
79  
80      public void setUp() throws Exception
81      {
82          dropDatabase();
83          startupDatabase( REPLICA_ID );
84          initStopWatch();
85      }
86  
87  
88      private void startupDatabase( String replicaId ) throws Exception
89      {
90          // Prepare configuration
91          ReplicationConfiguration cfg = new ReplicationConfiguration();
92          cfg.setReplicaId( replicaId );
93  
94          // Open store
95          store = new DerbyReplicationStore();
96          store.setTablePrefix( "TEST_" );
97          service = new DefaultDirectoryService();
98          service.setWorkingDirectory( DB_PATH );
99          store.open( service, cfg );
100     }
101 
102 
103     public void tearDown() throws Exception
104     {
105         store.close();
106         dropDatabase();
107     }
108 
109 
110     private void dropDatabase() throws IOException
111     {
112         FileUtils.deleteDirectory( DB_PATH );
113         File logFile = new File( "derby.log" );
114         if ( !logFile.delete() )
115         {
116             logFile.deleteOnExit();
117         }
118     }
119 
120 
121     public void testOperations() throws Exception
122     {
123         subTestReopen();
124         printElapsedTime( "Reopen" );
125         subTestUUID();
126         printElapsedTime( "UUID" );
127         subTestEmptyLog();
128         printElapsedTime( "EmptyLog" );
129         subTestWriteLog();
130         printElapsedTime( "WriteLog" );
131         subTestRemoveLogs();
132         printElapsedTime( "RemoveLogs" );
133         subTestVectors();
134         printElapsedTime( "Vectors" );
135     }
136 
137 
138     private void subTestReopen() throws Exception
139     {
140         store.close();
141         try
142         {
143             startupDatabase( OTHER_REPLICA_ID );
144             Assert.fail( "Store cannot start up with wrong replica ID." );
145         }
146         catch ( ReplicationStoreException e )
147         {
148         }
149         startupDatabase( REPLICA_ID );
150     }
151 
152 
153     private void subTestUUID() throws Exception
154     {
155         UUID uuid = UUID.randomUUID();
156         Name name = new LdapName( "ou=a, ou=b" );
157         Assert.assertTrue( store.putUUID( uuid, name ) );
158         Assert.assertEquals( name, store.getDN( uuid ) );
159         Assert.assertTrue( store.removeUUID( uuid ) );
160         Assert.assertFalse( store.removeUUID( uuid ) );
161         Assert.assertNull( store.getDN( uuid ) );
162     }
163 
164 
165     private void subTestEmptyLog() throws Exception
166     {
167         ReplicationLogIterator it;
168 
169         it = store.getLogs( csnFactory.newInstance( REPLICA_ID ), true );
170         Assert.assertFalse( it.next() );
171         it.close();
172         it = store.getLogs( csnFactory.newInstance( REPLICA_ID ), false );
173         Assert.assertFalse( it.next() );
174         it.close();
175         it = store.getLogs( csnFactory.newInstance( OTHER_REPLICA_ID ), true );
176         Assert.assertFalse( it.next() );
177         it.close();
178         it = store.getLogs( csnFactory.newInstance( OTHER_REPLICA_ID ), false );
179         Assert.assertFalse( it.next() );
180         it.close();
181 
182         Assert.assertEquals( 0, store.getLogSize() );
183     }
184 
185 
186     private void subTestWriteLog() throws Exception
187     {
188         Map<String, OidNormalizer> oids = new HashMap<String, OidNormalizer>();
189 
190         oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
191         oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
192         oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
193 
194         AttributeTypeRegistry atRegistry = service.getRegistries().getAttributeTypeRegistry();
195 
196         CSN csn = csnFactory.newInstance( REPLICA_ID );
197         CompositeOperation op1 = new CompositeOperation( service.getRegistries(), csn );
198         LdapDN ouA =  new LdapDN( "ou=a" ).normalize( oids );
199         op1.add( new AddEntryOperation( service.getRegistries(), csn, 
200             new DefaultServerEntry( service.getRegistries(), ouA ) ) );
201         
202         op1.add( new AddAttributeOperation( service.getRegistries(), csn, ouA, 
203             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
204         
205         op1.add( new ReplaceAttributeOperation( service.getRegistries(), csn, ouA, 
206             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
207         
208         op1.add( new DeleteAttributeOperation( service.getRegistries(), csn, ouA, 
209             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
210 
211         store.putLog( op1 );
212         testGetLogs( csn, op1 );
213 
214         csn = csnFactory.newInstance( OTHER_REPLICA_ID );
215         CompositeOperation op2 = new CompositeOperation( service.getRegistries(), csn );
216         op2.add( new AddEntryOperation( service.getRegistries(), csn, 
217             new DefaultServerEntry( service.getRegistries(), ouA ) ) );
218         
219         op2.add( new AddAttributeOperation( service.getRegistries(), csn, ouA, 
220             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
221         
222         op2.add( new ReplaceAttributeOperation( service.getRegistries(), csn, ouA, 
223             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
224         
225         op2.add( new DeleteAttributeOperation( service.getRegistries(), csn, ouA, 
226             new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
227 
228         store.putLog( op2 );
229         testGetLogs( csn, op2 );
230 
231         Assert.assertEquals( 2, store.getLogSize() );
232         Assert.assertEquals( 1, store.getLogSize( REPLICA_ID ) );
233         Assert.assertEquals( 1, store.getLogSize( OTHER_REPLICA_ID ) );
234 
235         // Test getLogs(CSNVector, true)
236         List<Operation> expected = new ArrayList<Operation>();
237         expected.add( op1 );
238         expected.add( op2 );
239         CSNVector updateVector = new CSNVector();
240         testGetLogs( updateVector, true, expected );
241 
242         updateVector = new CSNVector();
243         updateVector.setCSN( op1.getCSN() );
244         testGetLogs( updateVector, true, expected );
245         
246         updateVector = new CSNVector();
247         updateVector.setCSN( op2.getCSN() );
248         testGetLogs( updateVector, true, expected );
249         
250         updateVector = new CSNVector();
251         updateVector.setCSN( op1.getCSN() );
252         updateVector.setCSN( op2.getCSN() );
253         testGetLogs( updateVector, true, expected );
254 
255         // Test getLogs(CSNVector, false)
256         expected = new ArrayList<Operation>();
257         expected.add( op1 );
258         expected.add( op2 );
259         updateVector = new CSNVector();
260         testGetLogs( updateVector, false, expected );
261         expected = new ArrayList<Operation>();
262         expected.add( op2 );
263         updateVector = new CSNVector();
264         updateVector.setCSN( op1.getCSN() );
265         testGetLogs( updateVector, false, expected );
266         expected = new ArrayList<Operation>();
267         expected.add( op1 );
268         updateVector = new CSNVector();
269         updateVector.setCSN( op2.getCSN() );
270         testGetLogs( updateVector, false, expected );
271         expected = new ArrayList<Operation>();
272         updateVector = new CSNVector();
273         updateVector.setCSN( op1.getCSN() );
274         updateVector.setCSN( op2.getCSN() );
275         testGetLogs( updateVector, false, expected );
276     }
277 
278 
279     private void subTestRemoveLogs()
280     {
281         CSN csn;
282         ReplicationLogIterator it;
283 
284         it = store.getLogs( new DefaultCSN( 0, REPLICA_ID, 0 ), false );
285         it.next();
286         csn = it.getOperation( service.getRegistries() ).getCSN();
287         it.close();
288 
289         Assert.assertEquals( 0, store.removeLogs( csn, false ) );
290         Assert.assertEquals( 1, store.removeLogs( csn, true ) );
291         Assert.assertEquals( 0, store.getLogSize( REPLICA_ID ) );
292 
293         it = store.getLogs( new DefaultCSN( 0, OTHER_REPLICA_ID, 0 ), false );
294         Assert.assertTrue( it.next() );
295         csn = it.getOperation( service.getRegistries() ).getCSN();
296         it.close();
297 
298         Assert.assertEquals( 0, store.removeLogs( csn, false ) );
299         Assert.assertEquals( 1, store.removeLogs( csn, true ) );
300         Assert.assertEquals( 0, store.getLogSize( OTHER_REPLICA_ID ) );
301 
302         Assert.assertEquals( 0, store.getLogSize() );
303     }
304 
305 
306     private void subTestVectors() throws Exception
307     {
308         CSN csnA = new DefaultCSN( 0, REPLICA_ID, 0 );
309         CSN csnB = new DefaultCSN( 1, REPLICA_ID, 0 );
310         CSN csnC = new DefaultCSN( 0, OTHER_REPLICA_ID_2, 0 );
311         CSN csnD = new DefaultCSN( 0, OTHER_REPLICA_ID_2, 1 );
312         AttributeType at = service.getRegistries().getAttributeTypeRegistry().lookup( "ou" );
313         EntryAttribute attribute = new DefaultServerAttribute( at, "test" );
314         store.putLog( new AddAttributeOperation( service.getRegistries(), csnA, LdapDN.EMPTY_LDAPDN, attribute ) );
315         store.putLog( new AddAttributeOperation( service.getRegistries(), csnB, LdapDN.EMPTY_LDAPDN, attribute ) );
316         store.putLog( new AddAttributeOperation( service.getRegistries(), csnC, LdapDN.EMPTY_LDAPDN, attribute ) );
317         store.putLog( new AddAttributeOperation( service.getRegistries(), csnD, LdapDN.EMPTY_LDAPDN, attribute ) );
318 
319         Set<String> expectedKnownReplicaIds = new HashSet<String>();
320         expectedKnownReplicaIds.add( REPLICA_ID );
321         expectedKnownReplicaIds.add( OTHER_REPLICA_ID );
322         expectedKnownReplicaIds.add( OTHER_REPLICA_ID_2 );
323 
324         Assert.assertEquals( expectedKnownReplicaIds, store.getKnownReplicaIds() );
325 
326         CSNVector expectedUpdateVector = new CSNVector();
327         expectedUpdateVector.setCSN( csnB );
328         expectedUpdateVector.setCSN( csnD );
329 
330         Assert.assertEquals( expectedUpdateVector, store.getUpdateVector() );
331 
332         CSNVector expectedPurgeVector = new CSNVector();
333         expectedPurgeVector.setCSN( csnA );
334         expectedPurgeVector.setCSN( csnC );
335 
336         Assert.assertEquals( expectedPurgeVector, store.getPurgeVector() );
337     }
338 
339 
340     private void testGetLogs( CSN csn, Operation operation )
341     {
342         List<Operation> operations = new ArrayList<Operation>();
343         operations.add( operation );
344         testGetLogs( csn, operations );
345     }
346 
347 
348     private void testGetLogs( CSN csn, List<Operation> operations )
349     {
350         Iterator<Operation> it = operations.iterator();
351         ReplicationLogIterator rit = store.getLogs( csn, true );
352         testGetLogs( it, rit );
353 
354         rit = store.getLogs( csn, false );
355         Assert.assertFalse( rit.next() );
356         rit.close();
357     }
358 
359 
360     private void testGetLogs( CSNVector updateVector, boolean inclusive, List<Operation> operations )
361     {
362         Iterator<Operation> it = operations.iterator();
363         ReplicationLogIterator rit = store.getLogs( updateVector, inclusive );
364         testGetLogs( it, rit );
365     }
366 
367 
368     private void testGetLogs( Iterator<Operation> expectedIt, ReplicationLogIterator actualIt )
369     {
370         while ( expectedIt.hasNext() )
371         {
372             Operation expected = expectedIt.next();
373             Assert.assertTrue( actualIt.next() );
374 
375             Operation actual = actualIt.getOperation( service.getRegistries() );
376             Assert.assertEquals( expected.getCSN(), actual.getCSN() );
377             assertEquals( expected, actual );
378         }
379         Assert.assertFalse( actualIt.next() );
380         actualIt.close();
381     }
382 
383 
384     private void initStopWatch()
385     {
386         startTime = System.currentTimeMillis();
387     }
388 
389 
390     private void printElapsedTime( String testName )
391     {
392         long endTime = System.currentTimeMillis();
393         System.out.println( "Subtest #" + ( ++testCount ) + " [" + testName + "]: " + ( endTime - startTime ) + " ms" );
394         startTime = System.currentTimeMillis();
395     }
396 
397 
398     private static void assertEquals( Operation expected, Operation actual )
399     {
400         Assert.assertEquals( expected.toString(), actual.toString() );
401     }
402 }