Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 88014 invoked from network); 1 Mar 2010 19:58:40 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 1 Mar 2010 19:58:40 -0000 Received: (qmail 52093 invoked by uid 500); 1 Mar 2010 19:58:38 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 52034 invoked by uid 500); 1 Mar 2010 19:58:38 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 52027 invoked by uid 99); 1 Mar 2010 19:58:38 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 01 Mar 2010 19:58:38 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 01 Mar 2010 19:58:28 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id CA0D02388A3C; Mon, 1 Mar 2010 19:58:06 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r917683 - in /directory/apacheds/trunk: core-api/src/main/java/org/apache/directory/server/core/filtering/ core-integ/src/test/java/org/apache/directory/server/core/operations/search/ core/src/main/java/org/apache/directory/server/core/part... Date: Mon, 01 Mar 2010 19:58:06 -0000 To: commits@directory.apache.org From: kayyagari@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100301195806.CA0D02388A3C@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: kayyagari Date: Mon Mar 1 19:58:06 2010 New Revision: 917683 URL: http://svn.apache.org/viewvc?rev=917683&view=rev Log: o fix for DIRSERVER-1214 o a new cursor implementation to evaluate entries returned from various partitions o added test cases Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/filtering/CursorList.java Modified: directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/search/SearchIT.java directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/filtering/CursorList.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/filtering/CursorList.java?rev=917683&view=auto ============================================================================== --- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/filtering/CursorList.java (added) +++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/filtering/CursorList.java Mon Mar 1 19:58:06 2010 @@ -0,0 +1,490 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.directory.server.core.filtering; + + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.directory.server.core.entry.ClonedServerEntry; +import org.apache.directory.server.core.interceptor.context.SearchingOperationContext; +import org.apache.directory.shared.i18n.I18n; +import org.apache.directory.shared.ldap.cursor.ClosureMonitor; +import org.apache.directory.shared.ldap.cursor.Cursor; +import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException; +import org.apache.directory.shared.ldap.cursor.ListCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * An implementation of a Cursor based on a {@link List} of {@link Cursor}s. Optionally, the + * Cursor may be limited to a specific range within the list. + * + * This class is modeled based on the implementation of {@link ListCursor} + * + * WARN this is only used internally + * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public class CursorList implements EntryFilteringCursor +{ + /** The inner List */ + private final List list; + + /** The starting position for the cursor in the list. It can be > 0 */ + private final int start; + + /** The ending position for the cursor in the list. It can be < List.size() */ + private final int end; + + /** The current position in the list */ + private int index = -1; + + /** the operation context */ + private SearchingOperationContext opContext; + + /** flag to detect the closed cursor */ + private boolean closed; + + private static final Logger LOG = LoggerFactory.getLogger( CursorList.class ); + + + /** + * Creates a new ListCursor with lower (inclusive) and upper (exclusive) + * bounds. + * + * As with all Cursors, this ListCursor requires a successful return from + * advance operations (next() or previous()) to properly return values + * using the get() operation. + * + * @param start the lower bound index + * @param list the list this ListCursor operates on + * @param end the upper bound index + */ + public CursorList( int start, List list, int end, SearchingOperationContext opContext ) + { + if ( ( start < 0 ) || ( start > list.size() ) ) + { + throw new IllegalArgumentException( I18n.err( I18n.ERR_02005, start ) ); + } + + if ( ( end < 0 ) || ( end > list.size() ) ) + { + throw new IllegalArgumentException( I18n.err( I18n.ERR_02006, end ) ); + } + + // check list is not empty list since the empty list is the only situation + // where we allow for start to equal the end: in other cases it makes no sense + if ( ( list.size() > 0 ) && ( start >= end ) ) + { + throw new IllegalArgumentException( I18n.err( I18n.ERR_02007, start, end ) ); + } + + if ( list != null ) + { + this.list = list; + } + else + { + this.list = Collections.emptyList(); + } + + this.start = start; + this.end = end; + this.opContext = opContext; + } + + + /** + * Creates a new ListCursor without specific bounds: the bounds are + * acquired from the size of the list. + * + * @param list the backing for this ListCursor + */ + public CursorList( List list, SearchingOperationContext opContext ) + { + this( 0, list, list.size(), opContext ); + } + + + /** + * {@inheritDoc} + */ + public boolean available() + { + if ( index >= 0 && index < end ) + { + return list.get( index ).available(); + } + + return false; + } + + + /** + * @throws IllegalStateException if the underlying list is not sorted + * and/or a comparator is not provided. + */ + public void before( ClonedServerEntry element ) throws Exception + { + // checkNotClosed( "before()" ); + throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) ); + } + + + /** + * {@inheritDoc} + */ + public void after( ClonedServerEntry element ) throws Exception + { + throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) ); + } + + + /** + * {@inheritDoc} + */ + public void beforeFirst() throws Exception + { + this.index = -1; + list.get( index ).beforeFirst(); + } + + + /** + * {@inheritDoc} + */ + public void afterLast() throws Exception + { + this.index = end; + list.get( index ).afterLast(); + } + + + /** + * {@inheritDoc} + */ + public boolean first() throws Exception + { + if ( list.size() > 0 ) + { + index = start; + return list.get( index ).first(); + } + + return false; + } + + + /** + * {@inheritDoc} + */ + public boolean last() throws Exception + { + if ( list.size() > 0 ) + { + index = end - 1; + return list.get( index ).last(); + } + + return false; + } + + + /** + * {@inheritDoc} + */ + public boolean isFirst() throws Exception + { + return ( list.size() > 0 ) && ( index == start ) && list.get( index ).first(); + } + + + /** + * {@inheritDoc} + */ + public boolean isLast() throws Exception + { + return ( list.size() > 0 ) && ( index == end - 1 ) && list.get( index ).last(); + } + + + /** + * {@inheritDoc} + */ + public boolean isAfterLast() throws Exception + { + return index == end; + } + + + /** + * {@inheritDoc} + */ + public boolean isBeforeFirst() throws Exception + { + return index == -1; + } + + + /** + * {@inheritDoc} + */ + public boolean previous() throws Exception + { + // if parked at -1 we cannot go backwards + if ( index == -1 ) + { + return false; + } + + // if the index moved back is still greater than or eq to start then OK + if ( index - 1 >= start ) + { + if ( index == end ) + { + index--; + } + + if ( !list.get( index ).previous() ) + { + index--; + if ( index != -1 ) + { + return list.get( index ).previous(); + } + else + { + return false; + } + } + else + { + return true; + } + } + + // if the index currently less than or equal to start we need to park it at -1 and return false + if ( index <= start ) + { + if ( !list.get( index ).previous() ) + { + index = -1; + return false; + } + else + { + return true; + } + } + + if ( list.size() <= 0 ) + { + index = -1; + } + + return false; + } + + + /** + * {@inheritDoc} + */ + public boolean next() throws Exception + { + // if parked at -1 we advance to the start index and return true + if ( list.size() > 0 && index == -1 ) + { + index = start; + return list.get( index ).next(); + } + + // if the index plus one is less than the end then increment and return true + if ( list.size() > 0 && index + 1 < end ) + { + if ( !list.get( index ).next() ) + { + index++; + if ( index < end ) + { + return list.get( index ).next(); + } + else + { + return false; + } + } + else + { + return true; + } + } + + // if the index plus one is equal to the end then increment and return false + if ( list.size() > 0 && index + 1 == end ) + { + if ( !list.get( index ).next() ) + { + index++; + return false; + } + else + { + return true; + } + } + + if ( list.size() <= 0 ) + { + index = end; + } + + return false; + } + + + /** + * {@inheritDoc} + */ + public ClonedServerEntry get() throws Exception + { + if ( index < start || index >= end ) + { + throw new IOException( I18n.err( I18n.ERR_02009 ) ); + } + + if( list.get( index ).available() ) + { + return ( ClonedServerEntry ) list.get( index ).get(); + } + throw new InvalidCursorPositionException(); + } + + + /** + * {@inheritDoc} + */ + public boolean isElementReused() + { + return true; + } + + + public boolean addEntryFilter( EntryFilter filter ) + { + for( EntryFilteringCursor efc : list ) + { + efc.addEntryFilter( filter ); + } + + // returning hard coded value, shouldn't be a problem + return true; + } + + + public List getEntryFilters() + { + throw new UnsupportedOperationException( "CursorList doesn't support this operation" ); + } + + + public SearchingOperationContext getOperationContext() + { + return opContext; + } + + + public boolean isAbandoned() + { + return getOperationContext().isAbandoned(); + } + + + public boolean removeEntryFilter( EntryFilter filter ) + { + return false; + } + + + public void setAbandoned( boolean abandoned ) + { + getOperationContext().setAbandoned( abandoned ); + + if ( abandoned ) + { + LOG.info( "Cursor has been abandoned." ); + } + } + + + public void close() throws Exception + { + close( null ); + } + + + public void close( Exception reason ) throws Exception + { + closed = true; + for ( Cursor c : list ) + { + try + { + if ( reason != null ) + { + c.close(); + } + else + { + c.close( reason ); + } + } + catch ( Exception e ) + { + LOG.warn( "Failed to close the cursor" ); + } + } + } + + + public boolean isClosed() throws Exception + { + return closed; + } + + + public Iterator iterator() + { + throw new UnsupportedOperationException(); + } + + + public void setClosureMonitor( ClosureMonitor monitor ) + { + for ( Cursor c : list ) + { + c.setClosureMonitor( monitor ); + } + } + +} Modified: directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/search/SearchIT.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/search/SearchIT.java?rev=917683&r1=917682&r2=917683&view=diff ============================================================================== --- directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/search/SearchIT.java (original) +++ directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/search/SearchIT.java Mon Mar 1 19:58:06 2010 @@ -20,6 +20,7 @@ package org.apache.directory.server.core.operations.search; +import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext; import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext; import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext; import static org.junit.Assert.assertEquals; @@ -55,8 +56,6 @@ import org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException; import org.apache.directory.shared.ldap.ldif.LdifUtils; import org.apache.directory.shared.ldap.message.AliasDerefMode; -import org.apache.directory.shared.ldap.message.SearchRequestImpl; -import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -1738,5 +1737,104 @@ assertNull( attrs.get( SchemaConstants.ENTRY_UUID_AT ) ); assertNull( attrs.get( SchemaConstants.CREATORS_NAME_AT ) ); } + + + @Test + public void testSearchEmptyDNWithOneLevelScope() throws Exception + { + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + controls.setDerefLinkFlag( false ); + + LdapContext nullRootCtx = getRootContext( service ); + + NamingEnumeration list = nullRootCtx.search( "", "(objectClass=*)", controls ); + HashMap map = new HashMap(); + + while ( list.hasMore() ) + { + SearchResult result = list.next(); + map.put( result.getName(), result.getAttributes() ); + } + + assertEquals( 2, map.size() ); + + assertTrue( map.containsKey( "ou=system" ) ); + assertTrue( map.containsKey( "ou=schema" ) ); + } + + + @Test + public void testSearchEmptyDNWithSubLevelScope() throws Exception + { + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + controls.setDerefLinkFlag( false ); + + LdapContext nullRootCtx = getRootContext( service ); + + NamingEnumeration list = nullRootCtx.search( "", "(objectClass=organizationalUnit)", controls ); + HashMap map = new HashMap(); + + while ( list.hasMore() ) + { + SearchResult result = list.next(); + map.put( result.getName(), result.getAttributes() ); + } + + assertTrue( map.size() > 2 ); + + assertTrue( map.containsKey( "ou=system" ) ); + assertTrue( map.containsKey( "ou=schema" ) ); + } + + + @Test + public void testSearchEmptyDNWithObjectScopeAndNoObjectClassPresenceFilter() throws Exception + { + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.OBJECT_SCOPE ); + controls.setDerefLinkFlag( false ); + + LdapContext nullRootCtx = getRootContext( service ); + + NamingEnumeration list = nullRootCtx.search( "", "(objectClass=domain)", controls ); + HashMap map = new HashMap(); + + while ( list.hasMore() ) + { + SearchResult result = list.next(); + map.put( result.getName(), result.getAttributes() ); + } + + assertEquals( 0, map.size() ); + + assertFalse( map.containsKey( "ou=system" ) ); + assertFalse( map.containsKey( "ou=schema" ) ); + } + + @Test + public void testSearchEmptyDNWithOneLevelScopeAndNoObjectClassPresenceFilter() throws Exception + { + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + controls.setDerefLinkFlag( false ); + + LdapContext nullRootCtx = getRootContext( service ); + + NamingEnumeration list = nullRootCtx.search( "", "(cn=*)", controls ); + HashMap map = new HashMap(); + + while ( list.hasMore() ) + { + SearchResult result = list.next(); + map.put( result.getName(), result.getAttributes() ); + } + + assertEquals( 0, map.size() ); + + assertFalse( map.containsKey( "ou=system" ) ); + assertFalse( map.containsKey( "ou=schema" ) ); + } } Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java?rev=917683&r1=917682&r2=917683&view=diff ============================================================================== --- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java (original) +++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java Mon Mar 1 19:58:06 2010 @@ -46,6 +46,7 @@ import org.apache.directory.server.core.entry.DefaultServerEntry; import org.apache.directory.server.core.entry.ServerEntry; import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor; +import org.apache.directory.server.core.filtering.CursorList; import org.apache.directory.server.core.filtering.EntryFilteringCursor; import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext; import org.apache.directory.server.core.interceptor.context.AddOperationContext; @@ -81,6 +82,9 @@ import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl; import org.apache.directory.shared.ldap.constants.AuthenticationLevel; import org.apache.directory.shared.ldap.constants.SchemaConstants; +import org.apache.directory.shared.ldap.cursor.Cursor; +import org.apache.directory.shared.ldap.cursor.EmptyCursor; +import org.apache.directory.shared.ldap.cursor.ListCursor; import org.apache.directory.shared.ldap.cursor.SingletonCursor; import org.apache.directory.shared.ldap.entry.EntryAttribute; import org.apache.directory.shared.ldap.entry.Value; @@ -89,6 +93,7 @@ import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException; import org.apache.directory.shared.ldap.filter.ExprNode; import org.apache.directory.shared.ldap.filter.PresenceNode; +import org.apache.directory.shared.ldap.filter.SearchScope; import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect; import org.apache.directory.shared.ldap.name.LdapDN; import org.apache.directory.shared.ldap.schema.AttributeType; @@ -727,6 +732,10 @@ { boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE; + boolean isOnelevelScope = searchCtls.getSearchScope() == SearchControls.ONELEVEL_SCOPE; + + boolean isSublevelScope = searchCtls.getSearchScope() == SearchControls.SUBTREE_SCOPE; + // test for (objectClass=*) boolean isSearchAll = false; @@ -831,6 +840,39 @@ return new BaseEntryFilteringCursor( new SingletonCursor( serverEntry ), opContext ); } + else if ( isObjectScope && ( ! isSearchAll ) ) + { + return new BaseEntryFilteringCursor( new EmptyCursor(), opContext ); + } + else if( isOnelevelScope ) + { + List cursors = new ArrayList(); + for ( Partition p : partitions.values() ) + { + opContext.setDn( p.getSuffixDn() ); + opContext.setScope( SearchScope.OBJECT ); + cursors.add( p.search( opContext ) ); + } + + return new CursorList( cursors, opContext ); + } + else if ( isSublevelScope ) + { + List cursors = new ArrayList(); + for ( Partition p : partitions.values() ) + { + ClonedServerEntry entry = p.lookup( new LookupOperationContext( directoryService.getAdminSession(), p.getSuffixDn() ) ); + if( entry != null ) + { + Partition backend = getPartition( entry.getDn() ); + opContext.setDn( entry.getDn() ); + cursors.add( backend.search( opContext ) ); + } + } + + // don't feed the above Cursors' list to a BaseEntryFilteringCursor it is skipping the naming context entry of each partition + return new CursorList( cursors, opContext ); + } // TODO : handle searches based on the RootDSE throw new LdapNameNotFoundException();