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.service;
21  
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import javax.naming.NamingEnumeration;
26  import javax.naming.NamingException;
27  import javax.naming.directory.Attribute;
28  import javax.naming.directory.Attributes;
29  import javax.naming.directory.BasicAttribute;
30  import javax.naming.directory.BasicAttributes;
31  import javax.naming.directory.DirContext;
32  import javax.naming.directory.ModificationItem;
33  import javax.naming.ldap.LdapContext;
34  
35  import junit.framework.Assert;
36  
37  import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
38  import org.apache.directory.shared.ldap.name.LdapDN;
39  import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
40  import org.apache.directory.shared.ldap.schema.OidNormalizer;
41  import org.junit.Before;
42  import org.junit.Ignore;
43  import org.junit.Test;
44  
45  /**
46   * A test case for {@link ReplicationServiceITest}
47   * 
48   * @author The Apache Directory Project Team (dev@directory.apache.org)
49   * @version $Rev: 691179 $, $Date: 2008-09-02 11:58:45 +0200 (Di, 02 Sep 2008) $
50   */
51  public class ReplicationServiceITest extends AbstractReplicationServiceTestCase
52  {
53      private Map<String, OidNormalizer> oids;
54      
55      @Before public void setUp() throws Exception
56      {
57          createReplicas( new String[] { "A", "B", "C" } );
58  
59          
60          // Initialize OIDs maps for normalization
61          oids = new HashMap<String, OidNormalizer>();
62  
63          oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
64          oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
65          oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
66          oids.put( "cn", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
67          oids.put( "commonName", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
68          oids.put( "2.5.4.3", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
69      }
70  
71      @Ignore
72      @Test public void testOneWay() throws Exception
73      {
74          String dn1 = "cn=test,ou=system";
75          String dn2 = "cn=test2,ou=system";
76          testOneWayBind( dn1 );
77          testOneWayModify( dn1 );
78          testOneWayRename( dn1, dn2, true );
79          testOneWayRename( dn2, dn1, false );
80          testOneWayUnbind( dn1 );
81      }
82      
83      /**
84       * Test that the entry created last will win in the case of a conflict.
85       * 
86       * NOTE: This test is DISABLED as there is an occasional problem when a message is acknowledged
87       * too quickly, meaning no further messages can be sent until it has timed out (DIRSERVER-998).
88       *
89       * @throws Exception on failure
90       */
91      public void disabled_testTwoWayBind() throws Exception
92      {
93          LdapContext ctxA = getReplicaContext( "A" );
94          LdapContext ctxB = getReplicaContext( "B" );
95          LdapContext ctxC = getReplicaContext( "C" );
96  
97          Attributes entryA = new BasicAttributes( true );
98          entryA.put( "cn", "test" );
99          entryA.put( "sn", "test" );
100         entryA.put( "ou", "A" );
101         
102         Attribute oc = new BasicAttribute( "objectClass" );
103         oc.add( "top" );
104         oc.add( "person" );
105         oc.add( "organizationalPerson" );
106 
107         entryA.put( oc );
108         
109         ctxA.bind( "cn=test,ou=system", null, entryA );
110         
111         // Ensure the second bind is undebatebly the second.
112         Thread.sleep( 100 );
113 
114         Attributes entryB = new BasicAttributes( true );
115         entryB.put( "cn", "test" );
116         entryB.put( "sn", "test" );
117         entryB.put( "ou", "B" );
118         entryB.put( oc );
119         ctxB.bind( "cn=test,ou=system", null, entryB );
120 
121         // Let both replicas replicate.  Note that a replica can only receive
122         // logs from one peer at a time so we must delay between replications.
123         replicationServices.get( "A" ).replicate();
124         
125         Thread.sleep( 5000 );
126         
127         replicationServices.get( "B" ).replicate();
128         
129         Thread.sleep( 5000 );
130 
131         Assert.assertEquals( "B", getAttributeValue( ctxA, "cn=test,ou=system", "ou" ) );
132         Assert.assertEquals( "B", getAttributeValue( ctxB, "cn=test,ou=system", "ou" ) );
133         Assert.assertEquals( "B", getAttributeValue( ctxC, "cn=test,ou=system", "ou" ) );
134     }
135     
136     private void testOneWayBind( String dn ) throws Exception
137     {
138         LdapContext ctxA = getReplicaContext( "A" );
139         LdapContext ctxB = getReplicaContext( "B" );
140         LdapContext ctxC = getReplicaContext( "C" );
141         
142         Attributes entry = new BasicAttributes( true );
143         entry.put( "cn", "test" );
144         entry.put( "sn", "test" );
145         
146         Attribute oc = new BasicAttribute( "objectClass" );
147         oc.add( "top" );
148         oc.add( "person" );
149         oc.add( "organizationalPerson" );
150         
151         entry.put( oc );
152         
153         ctxA.bind( dn, null, entry );
154 
155         replicationServices.get( "A" ).replicate();
156         
157         Thread.sleep( 5000 );
158 
159         Assert.assertNotNull( ctxA.lookup( dn ) );
160         Assert.assertNotNull( ctxB.lookup( dn ) );
161         Assert.assertNotNull( ctxC.lookup( dn ) );
162     }
163 
164     private void testOneWayModify( String dn ) throws Exception
165     {
166         LdapContext ctxA = getReplicaContext( "A" );
167         LdapContext ctxB = getReplicaContext( "B" );
168         LdapContext ctxC = getReplicaContext( "C" );
169         
170         String newValue = "anything";
171         
172         ctxA.modifyAttributes( dn, new ModificationItem[] {
173             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute( "ou", newValue ))} );
174 
175         replicationServices.get( "A" ).replicate();
176         
177         Thread.sleep( 5000 );
178 
179         Assert.assertEquals( newValue, getAttributeValue( ctxB, dn, "ou" ) );
180         Assert.assertEquals( newValue, getAttributeValue( ctxC, dn, "ou" ) );
181     }
182 
183     private void testOneWayRename( String dn1, String dn2, boolean deleteRDN ) throws Exception
184     {
185         LdapContext ctxA = getReplicaContext( "A" );
186         LdapContext ctxB = getReplicaContext( "B" );
187         LdapContext ctxC = getReplicaContext( "C" );
188         
189         String oldRDNValue = (String) new LdapDN(dn1).getRdn().getUpValue();
190         
191         ctxA.addToEnvironment( "java.naming.ldap.deleteRDN", Boolean.toString( deleteRDN ) );
192         ctxA.rename( dn1, dn2 );
193         
194         replicationServices.get( "A" ).replicate();
195 
196         Thread.sleep( 5000 );
197         
198         assertNotExists( ctxA, dn1 );
199         assertNotExists( ctxB, dn1 );
200         assertNotExists( ctxC, dn1 );
201         Assert.assertNotNull( ctxA.lookup( dn2 ) );
202         Assert.assertNotNull( ctxB.lookup( dn2 ) );
203         Assert.assertNotNull( ctxC.lookup( dn2 ) );
204 
205         Attribute oldRDNAttributeA = ctxA.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
206         Attribute oldRDNAttributeB = ctxB.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
207         Attribute oldRDNAttributeC = ctxC.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
208         boolean oldRDNExistsA = attributeContainsValue( oldRDNAttributeA, oldRDNValue );
209         boolean oldRDNExistsB = attributeContainsValue( oldRDNAttributeB, oldRDNValue );
210         boolean oldRDNExistsC = attributeContainsValue( oldRDNAttributeC, oldRDNValue );
211         
212         if ( deleteRDN )
213         {
214             Assert.assertFalse( oldRDNExistsA );
215             Assert.assertFalse( oldRDNExistsB );
216             Assert.assertFalse( oldRDNExistsC );
217         }
218         else
219         {
220             Assert.assertTrue( oldRDNExistsA );
221             Assert.assertTrue( oldRDNExistsB );
222             Assert.assertTrue( oldRDNExistsC );
223         }
224     }
225     
226     private void testOneWayUnbind( String dn ) throws Exception
227     {
228         LdapContext ctxA = getReplicaContext( "A" );
229         LdapContext ctxB = getReplicaContext( "B" );
230         LdapContext ctxC = getReplicaContext( "C" );
231         
232         ctxA.unbind( dn );
233         
234         replicationServices.get( "A" ).replicate();
235 
236         Thread.sleep( 5000 );
237         
238         assertNotExists( ctxA, dn );
239         assertNotExists( ctxB, dn );
240         assertNotExists( ctxC, dn );
241     }
242     
243     private void assertNotExists( LdapContext ctx, String dn ) throws NamingException
244     {
245         try
246         {
247             ctx.lookup( dn );
248         }
249         catch ( LdapNameNotFoundException e )
250         {
251             // This is expected so return immediately.
252             return;
253         }
254         throw new AssertionError( "The entry exists" );
255     }
256     
257     private String getAttributeValue( LdapContext ctx, String name, String attrName ) throws Exception
258     {
259         Attribute attr = ctx.getAttributes( name ).get( attrName );
260         return ( String ) attr.get();
261     }
262     
263     private boolean attributeContainsValue( Attribute attribute, Object value ) throws NamingException
264     {
265         boolean foundValue = false;
266         for ( NamingEnumeration ne = attribute.getAll(); ne.hasMore(); )
267         {
268             if ( value.equals( ne.next() ) )
269             {
270                 foundValue = true;
271             }
272         }
273         return foundValue;
274     }
275 }