/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2009 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
package org.opends.server.extensions;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.api.DirectoryThread;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchListener;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.SearchRequest;
import static org.opends.server.protocols.internal.Requests.*;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.LDAPURL;
import org.opends.server.types.MembershipException;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import static org.opends.messages.ExtensionMessages.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
/**
* This class implements a Directory Server thread that will be used to perform
* a background search to retrieve all of the members of a dynamic group.
* <BR><BR>
*/
public class DynamicGroupSearchThread
// FIXME -- Would it be better to implement this class using an Executor
// rather than always creating a custom thread?
extends DirectoryThread
implements InternalSearchListener
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The set of base DNs for the search requests. */
private final DN[] baseDNs;
/** The member list with which this search thread is associated. */
private final DynamicGroupMemberList memberList;
/** A counter used to keep track of which search is currently in progress. */
private int searchCounter;
/** The set of member URLs for determining whether entries match the criteria. */
private final LDAPURL[][] memberURLs;
/** The set of search filters for the search requests. */
private final SearchFilter[] searchFilters;
/**
* Creates a new dynamic group search thread that is associated with the
* provided member list and that will perform the search using the provided
* information.
*
* @param memberList The dynamic group member list with which this thread is
* associated.
* @param baseDNs The set of base DNs to use for the search requests.
* @param filters The set of search filters to use for the search
* requests.
* @param memberURLs The set of member URLs to use when determining if
* entries match the necessary group criteria.
*/
public DynamicGroupSearchThread(DynamicGroupMemberList memberList,
DN[] baseDNs, SearchFilter[] filters,
LDAPURL[][] memberURLs)
{
super("Dynamic Group Search Thread " + memberList.getDynamicGroupDN());
this.memberList = memberList;
this.baseDNs = baseDNs;
this.searchFilters = filters;
this.memberURLs = memberURLs;
searchCounter = 0;
}
/**
* Performs the set of searches and provides the results to the associated
* member list.
*/
@Override
public void run()
{
InternalClientConnection conn = getRootConnection();
for (searchCounter = 0; searchCounter < baseDNs.length; searchCounter++)
{
DN baseDN = baseDNs[searchCounter];
SearchFilter filter = searchFilters[searchCounter];
// Include all the user attributes along with the ismemberof.
final SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, filter)
.addAttribute("*", "ismemberof");
InternalSearchOperation searchOperation = conn.processSearch(request, this);
ResultCode resultCode = searchOperation.getResultCode();
if (resultCode != ResultCode.SUCCESS)
{
if (resultCode == ResultCode.NO_SUCH_OBJECT)
{
logger.warn(WARN_DYNAMICGROUP_NONEXISTENT_BASE_DN, baseDN,
memberList.getDynamicGroupDN());
continue;
}
else
{
LocalizableMessage message =
ERR_DYNAMICGROUP_INTERNAL_SEARCH_FAILED.get(
baseDN,
filter,
memberList.getDynamicGroupDN(),
resultCode,
searchOperation.getErrorMessage());
if (! memberList.addResult(
new MembershipException(message, true)))
{
memberList.setSearchesCompleted();
return;
}
}
}
}
memberList.setSearchesCompleted();
}
/** {@inheritDoc} */
@Override
public void handleInternalSearchEntry(InternalSearchOperation searchOperation,
SearchResultEntry searchEntry)
throws DirectoryException
{
for (LDAPURL url : memberURLs[searchCounter])
{
if (url.matchesEntry(searchEntry))
{
if (! memberList.addResult(searchEntry))
{
LocalizableMessage message = ERR_DYNAMICGROUP_CANNOT_RETURN_ENTRY.
get(searchEntry.getName(), memberList.getDynamicGroupDN());
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
}
return;
}
}
}
/** {@inheritDoc} */
@Override
public void handleInternalSearchReference(
InternalSearchOperation searchOperation,
SearchResultReference searchReference)
{
// No implementation required.
}
}