public static boolean testAttributeReplication( final ChaiEntry chaiEntry, final String attribute, final String value )
            throws ChaiOperationException, ChaiUnavailableException
    {
        final String effectiveValue = ( value == null || value.length() < 1 )
                ? chaiEntry.readStringAttribute( attribute )
                : value;


        if ( effectiveValue == null )
        {
            throw ChaiOperationException.forErrorMessage( "unreadable to read test attribute from primary ChaiProvider" );
        }

        final ChaiConfiguration chaiConfiguration = chaiEntry.getChaiProvider().getChaiConfiguration();

        final List<String> ldapURLs = chaiConfiguration.bindURLsAsList();

        LOGGER.trace( "testAttributeReplication, will test the following ldap urls: " + ldapURLs );

        int testCount = 0;
        int successCount = 0;

        final Collection<ChaiConfiguration> perReplicaProviders = splitConfigurationPerReplica(
                chaiEntry.getChaiProvider().getChaiConfiguration(),
                Collections.singletonMap( ChaiSetting.FAILOVER_CONNECT_RETRIES, "1" )
        );

        for ( final ChaiConfiguration loopConfiguration : perReplicaProviders )
        {
            ChaiProvider loopProvider = null;
            try
            {
                loopProvider = chaiEntry.getChaiProvider().getProviderFactory().newProvider( loopConfiguration );
                if ( loopProvider.compareStringAttribute( chaiEntry.getEntryDN(), attribute, effectiveValue ) )
                {
                    successCount++;
                }

                testCount++;
            }
            catch ( ChaiUnavailableException e )
            {
                //disregard
            }
            catch ( ChaiOperationException e )
            {
                //disregard
            }
            finally
            {
                try
                {
                    if ( loopProvider != null )
                    {
                        loopProvider.close();
                    }
                }
                catch ( Exception e )
                {
                    //already closed, whatever.
                }
            }
        }

        if ( LOGGER.isDebugEnabled() )
        {
            final StringBuilder debugMsg = new StringBuilder();
            debugMsg.append( "testAttributeReplication for " ).append( chaiEntry ).append( ":" ).append( attribute );
            debugMsg.append( " " ).append( testCount ).append( " up," );
            debugMsg.append( " " ).append( ldapURLs.size() - testCount ).append( " down," );
            debugMsg.append( " " ).append( successCount ).append( " in sync" );
            LOGGER.debug( debugMsg );
        }

        return testCount > 0 && testCount == successCount;
    }
public static boolean testAttributeReplication ( final ChaiEntry chaiEntry , final String attribute , final String value ) throws ChaiOperationException , ChaiUnavailableException { final String effectiveValue = ( value == null || value . length ( ) < 1 ) ? chaiEntry . readStringAttribute ( attribute ) : value ; if ( effectiveValue == null ) { throw ChaiOperationException . forErrorMessage ( "unreadable to read test attribute from primary ChaiProvider" ) ; } final ChaiConfiguration chaiConfiguration = chaiEntry . getChaiProvider ( ) . getChaiConfiguration ( ) ; final List < String > ldapURLs = chaiConfiguration . bindURLsAsList ( ) ; LOGGER . trace ( "testAttributeReplication, will test the following ldap urls: " + ldapURLs ) ; int testCount = 0 ; int successCount = 0 ; final Collection < ChaiConfiguration > perReplicaProviders = splitConfigurationPerReplica ( chaiEntry . getChaiProvider ( ) . getChaiConfiguration ( ) , Collections . singletonMap ( ChaiSetting . FAILOVER_CONNECT_RETRIES , "1" ) ) ; for ( final ChaiConfiguration loopConfiguration : perReplicaProviders ) { ChaiProvider loopProvider = null ; try { loopProvider = chaiEntry . getChaiProvider ( ) . getProviderFactory ( ) . newProvider ( loopConfiguration ) ; if ( loopProvider . compareStringAttribute ( chaiEntry . getEntryDN ( ) , attribute , effectiveValue ) ) { successCount ++ ; } testCount ++ ; } catch ( ChaiUnavailableException e ) { } catch ( ChaiOperationException e ) { } finally { try { if ( loopProvider != null ) { loopProvider . close ( ) ; } } catch ( Exception e ) { } } } if ( LOGGER . isDebugEnabled ( ) ) { final StringBuilder debugMsg = new StringBuilder ( ) ; debugMsg . append ( "testAttributeReplication for " ) . append ( chaiEntry ) . append ( ":" ) . append ( attribute ) ; debugMsg . append ( " " ) . append ( testCount ) . append ( " up," ) ; debugMsg . append ( " " ) . append ( ldapURLs . size ( ) - testCount ) . append ( " down," ) ; debugMsg . append ( " " ) . append ( successCount ) . append ( " in sync" ) ; LOGGER . debug ( debugMsg ) ; } return testCount > 0 && testCount == successCount ; }
<p>Test the replication of an attribute.  It is left to the implementation to determine the means and criteria for
this operation.  Typically this method would be used just after a write operation in some type of time delayed loop.
This method does not write any data to the directory.</p>

<p>Typical implementations will do the following:</p>
<ul>
<li>issue {@link com.novell.ldapchai.ChaiEntry#readStringAttribute(String)} to read a value</li>
<li>establish an LDAP connection to all known replicas</li>
<li>issue {@link com.novell.ldapchai.ChaiEntry#compareStringAttribute(String, String)} to to each server directly</li>
<li>return true if each server contacted has the same value, false if not</li>
</ul>

<p>Target servers that are unreachable or return errors are ignored, and do not influence the results. It is entirely
possible that no matter how many times this method is called, false will always be returned, so the caller should
take care not to repeat a test indefinitely.</p>

<p>This operation is potentially expensive, as it may establish new LDAP level connections to each target server each
time it is invoked.</p>

<p>The following sample shows how this method might be used.  There are a few important attributes of the sample:</p>
<ul>
<li>Multiple ldap servers are specified</li>
<li>There is a pause time between each replication check (the test can be expensive)</li>
<li>There is a timeout period (the test may never successfully complete)</li>
</ul>
<p><b>Example Usage:</b></p>
<pre>
// write a timestamp value to an attribute
theUser.writeStringAttributes("description","testValue" + Instant.now().toString());

// maximum time to wait for replication
final int maximumWaitTime = 120 * 1000;

// time between iterations
final int pauseTime = 3 * 1000;

// timestamp of beginning of wait
final long startTime = System.currentTimeMillis();

boolean replicated = false;

// loop until
while (System.currentTimeMillis() - startTime &lt; maximumWaitTime) {

// sleep between iterations
try { Thread.sleep(pauseTime); } catch (InterruptedException e)  {}

// check if data replicated yet
replicated = ChaiUtility.testAttributeReplication(theUser,"description",null);

// break if data has replicated
if (replicated) {
break;
}
}

// report success
System.out.println("Attribute replication successful: " + replicated);
</pre>

@param chaiEntry A valid entry
@param attribute A valid attribute on the entry
@param value     The value to test for.  If {@code null}, a value is read from the active server
@return true if the attribute is the same on all servers
@throws ChaiOperationException   If an error is encountered during the operation
@throws ChaiUnavailableException If no directory servers are reachable
@throws IllegalStateException    If the underlying connection is not in an available state