/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://IdentityConnectors.dev.java.net/legal/license.txt * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.ldap.search; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.logging.LogSpi; import org.identityconnectors.common.logging.Log.Level; import org.identityconnectors.framework.api.APIConfiguration; import org.identityconnectors.framework.api.ConnectorFacade; import org.identityconnectors.framework.api.ConnectorFacadeFactory; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.ldap.LdapConfiguration; import org.identityconnectors.ldap.LdapConnection; import org.identityconnectors.ldap.LdapConnector; import org.identityconnectors.ldap.SunDSTestBase; import org.identityconnectors.test.common.TestHelpers; import org.identityconnectors.test.common.ToListResultsHandler; public class VlvIndexSearchStrategyTests extends SunDSTestBase { public static LdapConfiguration newConfiguration(int blockSize) { LdapConfiguration config = newConfiguration(); config.setUseBlocks(true); config.setBlockSize(blockSize); config.setUsePagedResultControl(false); config.validate(); return config; } /** * The VLV index specification allows for rounding errors when computing * the server target index (see the "Si = Sc * (Ci / Cc)" computation in * draft-ietf-ldapext-ldapv3-vlv-09.txt). This test ensures the VLV index * search strategy works around these rounding errors. */ @Test public void testOverlapWorkaround() throws Exception { // Rounding errors occur with small block sizes, so using a size of 2. LdapConfiguration config = newConfiguration(2); LdapConnection conn = new LdapConnection(config); cleanupBaseContext(conn); conn.close(); ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance(); APIConfiguration impl = TestHelpers.createTestConfiguration(LdapConnector.class, config); ConnectorFacade facade = factory.newInstance(impl); // When Sc and Cc are 90 and the block size is 2, rounding // errors occur at indexes 27, 50, 53, and 64. final int COUNT = 90; String baseContext = config.getBaseContexts()[0]; for (int i = 0; i < COUNT; i++) { String userName = "user." + i; Set<Attribute> attrs = new HashSet<Attribute>(); attrs.add(new Name("uid=" + userName + "," + baseContext)); attrs.add(AttributeBuilder.build("uid", userName)); attrs.add(AttributeBuilder.build("cn", userName)); attrs.add(AttributeBuilder.build("sn", userName)); facade.create(ObjectClass.ACCOUNT, attrs, null); } ToListResultsHandler handler = new ToListResultsHandler(); Log oldLog = VlvIndexSearchStrategy.getLog(); OverlapLogImpl overlapLog = new OverlapLogImpl(oldLog); try { VlvIndexSearchStrategy.setLog(createLog(VlvIndexSearchStrategy.class, overlapLog)); facade.search(ObjectClass.ACCOUNT, null, handler, null); } finally { VlvIndexSearchStrategy.setLog(oldLog); } assertTrue("The server should have sent overlapping blocks", overlapLog.hasOverlap()); assertEquals(COUNT, handler.getObjects().size()); } private static Log createLog(Class<?> clazz, LogSpi spi) throws Exception { Method getLogMethod = Log.class.getDeclaredMethod("getLog", Class.class, LogSpi.class); getLogMethod.setAccessible(true); return (Log) getLogMethod.invoke(null, clazz, spi); } static class OverlapLogImpl implements LogSpi { private final Log delegate; private boolean overlap = false; public OverlapLogImpl(Log delegate) { this.delegate = delegate; } public boolean isLoggable(Class<?> clazz, Level level) { return true; // We need to, in order to ensure log() will be called. } public void log(Class<?> clazz, String method, Level level, String message, Throwable ex) { if (VlvIndexSearchStrategy.class.equals(clazz) && message != null && message.contains("overlap")) { overlap = true; } if (delegate.isLoggable(level)) { delegate.log(clazz, method, level, message, ex); } } public boolean hasOverlap() { return overlap; } } }