mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
15.50.2014 45141fb11ef698b11c6fb3becca82ca10e11505a
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
@@ -11,18 +11,10 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2012-2013 ForgeRock AS.
 * Copyright 2012-2014 ForgeRock AS.
 */
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
import static org.forgerock.opendj.rest2ldap.Utils.i18n;
import static org.forgerock.opendj.rest2ldap.Utils.transform;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -54,13 +46,19 @@
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.util.promise.FailureHandler;
import org.forgerock.util.promise.SuccessHandler;
import static org.forgerock.opendj.ldap.ErrorResultException.*;
import static org.forgerock.opendj.ldap.requests.Requests.*;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
import static org.forgerock.opendj.rest2ldap.Utils.*;
/**
 * An attribute mapper which provides a mapping from a JSON value to a single DN
 * valued LDAP attribute.
 */
public final class ReferenceAttributeMapper extends
        AbstractLDAPAttributeMapper<ReferenceAttributeMapper> {
public final class ReferenceAttributeMapper extends AbstractLDAPAttributeMapper<ReferenceAttributeMapper> {
    /**
     * The maximum number of candidate references to allow in search filters.
     */
@@ -73,7 +71,7 @@
    private SearchScope scope = SearchScope.WHOLE_SUBTREE;
    ReferenceAttributeMapper(final AttributeDescription ldapAttributeName, final DN baseDN,
            final AttributeDescription primaryKey, final AttributeMapper mapper) {
        final AttributeDescription primaryKey, final AttributeMapper mapper) {
        super(ldapAttributeName);
        this.baseDN = baseDN;
        this.primaryKey = primaryKey;
@@ -127,65 +125,65 @@
    }
    @Override
    void getLDAPFilter(final Context c, final JsonPointer path, final JsonPointer subPath,
            final FilterType type, final String operator, final Object valueAssertion,
            final ResultHandler<Filter> h) {
    void getLDAPFilter(final Context c, final JsonPointer path, final JsonPointer subPath, final FilterType type,
        final String operator, final Object valueAssertion, final ResultHandler<Filter> h) {
        // Construct a filter which can be used to find referenced resources.
        mapper.getLDAPFilter(c, path, subPath, type, operator, valueAssertion,
                new ResultHandler<Filter>() {
        mapper.getLDAPFilter(c, path, subPath, type, operator, valueAssertion, new ResultHandler<Filter>() {
            @Override
            public void handleError(final ResourceException error) {
                h.handleError(error); // Propagate.
            }
            @Override
            public void handleResult(final Filter result) {
                // Search for all referenced entries and construct a filter.
                final SearchRequest request = createSearchRequest(result);
                final List<Filter> subFilters = new LinkedList<Filter>();
                final FailureHandler<ErrorResultException> failureHandler =
                    new FailureHandler<ErrorResultException>() {
                        @Override
                        public void handleError(ErrorResultException error) {
                            h.handleError(asResourceException(error)); // Propagate.
                        }
                    };
                c.getConnection().searchAsync(request, new SearchResultHandler() {
                    @Override
                    public void handleError(final ResourceException error) {
                        h.handleError(error); // Propagate.
                    public boolean handleEntry(final SearchResultEntry entry) {
                        if (subFilters.size() < SEARCH_MAX_CANDIDATES) {
                            subFilters.add(Filter.equality(ldapAttributeName.toString(), entry.getName()));
                            return true;
                        } else {
                            // No point in continuing - maximum candidates reached.
                            return false;
                        }
                    }
                    @Override
                    public void handleResult(final Filter result) {
                        // Search for all referenced entries and construct a filter.
                        final SearchRequest request = createSearchRequest(result);
                        c.getConnection().searchAsync(request, null, new SearchResultHandler() {
                            final List<Filter> subFilters = new LinkedList<Filter>();
                            @Override
                            public boolean handleEntry(final SearchResultEntry entry) {
                                if (subFilters.size() < SEARCH_MAX_CANDIDATES) {
                                    subFilters.add(Filter.equality(ldapAttributeName.toString(),
                                            entry.getName()));
                                    return true;
                                } else {
                                    // No point in continuing - maximum candidates reached.
                                    return false;
                                }
                            }
                            @Override
                            public void handleErrorResult(final ErrorResultException error) {
                                h.handleError(asResourceException(error)); // Propagate.
                            }
                            @Override
                            public boolean handleReference(final SearchResultReference reference) {
                                // Ignore references.
                                return true;
                            }
                            @Override
                            public void handleResult(final Result result) {
                                if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
                                    handleErrorResult(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
                                } else if (subFilters.size() == 1) {
                                    h.handleResult(subFilters.get(0));
                                } else {
                                    h.handleResult(Filter.or(subFilters));
                                }
                            }
                        });
                    public boolean handleReference(final SearchResultReference reference) {
                        // Ignore references.
                        return true;
                    }
                });
                }).onSuccess(new SuccessHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
                            failureHandler.handleError(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
                        } else if (subFilters.size() == 1) {
                            h.handleResult(subFilters.get(0));
                        } else {
                            h.handleResult(Filter.or(subFilters));
                        }
                    }
                }).onFailure(failureHandler);
            }
        });
    }
    @Override
    void getNewLDAPAttributes(final Context c, final JsonPointer path,
            final List<Object> newValues, final ResultHandler<Attribute> h) {
    void getNewLDAPAttributes(final Context c, final JsonPointer path, final List<Object> newValues,
        final ResultHandler<Attribute> h) {
        /*
         * For each value use the subordinate mapper to obtain the LDAP primary
         * key, the perform a search for each one to find the corresponding
@@ -193,8 +191,7 @@
         */
        final Attribute newLDAPAttribute = new LinkedAttribute(ldapAttributeName);
        final AtomicInteger pendingSearches = new AtomicInteger(newValues.size());
        final AtomicReference<ResourceException> exception =
                new AtomicReference<ResourceException>();
        final AtomicReference<ResourceException> exception = new AtomicReference<ResourceException>();
        for (final Object value : newValues) {
            mapper.create(c, path, new JsonValue(value), new ResultHandler<List<Attribute>>() {
@@ -216,17 +213,15 @@
                    if (primaryKeyAttribute == null || primaryKeyAttribute.isEmpty()) {
                        h.handleError(new BadRequestException(i18n(
                                "The request cannot be processed because the reference "
                                        + "field '%s' contains a value which does not contain "
                                        + "a primary key", path)));
                            "The request cannot be processed because the reference "
                                + "field '%s' contains a value which does not contain " + "a primary key", path)));
                        return;
                    }
                    if (primaryKeyAttribute.size() > 1) {
                        h.handleError(new BadRequestException(i18n(
                                "The request cannot be processed because the reference "
                                        + "field '%s' contains a value which contains multiple "
                                        + "primary keys", path)));
                            "The request cannot be processed because the reference "
                                + "field '%s' contains a value which contains multiple " + "primary keys", path)));
                        return;
                    }
@@ -234,45 +229,39 @@
                    final ByteString primaryKeyValue = primaryKeyAttribute.firstValue();
                    final Filter filter = Filter.equality(primaryKey.toString(), primaryKeyValue);
                    final SearchRequest search = createSearchRequest(filter);
                    c.getConnection().searchSingleEntryAsync(search,
                            new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
                    c.getConnection().searchSingleEntryAsync(search).onSuccess(new SuccessHandler<SearchResultEntry>() {
                        @Override
                        public void handleResult(final SearchResultEntry result) {
                            synchronized (newLDAPAttribute) {
                                newLDAPAttribute.add(result.getName());
                            }
                            completeIfNecessary();
                        }
                    }).onFailure(new FailureHandler<ErrorResultException>() {
                        @Override
                        public void handleError(final ErrorResultException error) {
                            ResourceException re;
                            try {
                                throw error;
                            } catch (final EntryNotFoundException e) {
                                re =
                                    new BadRequestException(i18n("The request cannot be processed "
                                        + "because the resource '%s' " + "referenced in field '%s' does "
                                        + "not exist", primaryKeyValue.toString(), path));
                            } catch (final MultipleEntriesFoundException e) {
                                re =
                                    new BadRequestException(i18n(
                                        "The request cannot be processed " + "because the resource '%s' "
                                            + "referenced in field '%s' is " + "ambiguous",
                                        primaryKeyValue.toString(), path));
                            } catch (final ErrorResultException e) {
                                re = asResourceException(e);
                            }
                            exception.compareAndSet(null, re);
                            completeIfNecessary();
                        }
                                @Override
                                public void handleErrorResult(final ErrorResultException error) {
                                    ResourceException re;
                                    try {
                                        throw error;
                                    } catch (final EntryNotFoundException e) {
                                        re =
                                                new BadRequestException(i18n(
                                                        "The request cannot be processed "
                                                                + "because the resource '%s' "
                                                                + "referenced in field '%s' does "
                                                                + "not exist", primaryKeyValue
                                                                .toString(), path));
                                    } catch (final MultipleEntriesFoundException e) {
                                        re =
                                                new BadRequestException(i18n(
                                                        "The request cannot be processed "
                                                                + "because the resource '%s' "
                                                                + "referenced in field '%s' is "
                                                                + "ambiguous", primaryKeyValue
                                                                .toString(), path));
                                    } catch (final ErrorResultException e) {
                                        re = asResourceException(e);
                                    }
                                    exception.compareAndSet(null, re);
                                    completeIfNecessary();
                                }
                                @Override
                                public void handleResult(final SearchResultEntry result) {
                                    synchronized (newLDAPAttribute) {
                                        newLDAPAttribute.add(result.getName());
                                    }
                                    completeIfNecessary();
                                }
                            });
                    });
                }
                private void completeIfNecessary() {
@@ -294,8 +283,7 @@
    }
    @Override
    void read(final Context c, final JsonPointer path, final Entry e,
            final ResultHandler<JsonValue> h) {
    void read(final Context c, final JsonPointer path, final Entry e, final ResultHandler<JsonValue> h) {
        final Attribute attribute = e.getAttribute(ldapAttributeName);
        if (attribute == null || attribute.isEmpty()) {
            h.handleResult(null);
@@ -309,30 +297,27 @@
            }
        } else {
            try {
                final Set<DN> dns =
                        attribute.parse().usingSchema(c.getConfig().schema()).asSetOfDN();
                final Set<DN> dns = attribute.parse().usingSchema(c.getConfig().schema()).asSetOfDN();
                final ResultHandler<JsonValue> handler =
                        accumulate(dns.size(), transform(
                                new Function<List<JsonValue>, JsonValue, Void>() {
                                    @Override
                                    public JsonValue apply(final List<JsonValue> value, final Void p) {
                                        if (value.isEmpty()) {
                                            /*
                                             * No values, so omit the entire
                                             * JSON object from the resource.
                                             */
                                            return null;
                                        } else {
                                            // Combine values into a single JSON array.
                                            final List<Object> result =
                                                    new ArrayList<Object>(value.size());
                                            for (final JsonValue e : value) {
                                                result.add(e.getObject());
                                            }
                                            return new JsonValue(result);
                                        }
                                    }
                                }, h));
                    accumulate(dns.size(), transform(new Function<List<JsonValue>, JsonValue, Void>() {
                        @Override
                        public JsonValue apply(final List<JsonValue> value, final Void p) {
                            if (value.isEmpty()) {
                                /*
                                 * No values, so omit the entire JSON object
                                 * from the resource.
                                 */
                                return null;
                            } else {
                                // Combine values into a single JSON array.
                                final List<Object> result = new ArrayList<Object>(value.size());
                                for (final JsonValue e : value) {
                                    result.add(e.getObject());
                                }
                                return new JsonValue(result);
                            }
                        }
                    }, h));
                for (final DN dn : dns) {
                    readEntry(c, path, dn, handler);
                }
@@ -349,14 +334,18 @@
    }
    private void readEntry(final Context c, final JsonPointer path, final DN dn,
            final ResultHandler<JsonValue> handler) {
        final ResultHandler<JsonValue> handler) {
        final Set<String> requestedLDAPAttributes = new LinkedHashSet<String>();
        mapper.getLDAPAttributes(c, path, new JsonPointer(), requestedLDAPAttributes);
        c.getConnection().readEntryAsync(dn, requestedLDAPAttributes,
                new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
        c.getConnection().readEntryAsync(dn, requestedLDAPAttributes)
                .onSuccess(new SuccessHandler<SearchResultEntry>() {
                    @Override
                    public void handleErrorResult(final ErrorResultException error) {
                    public void handleResult(final SearchResultEntry result) {
                        mapper.read(c, path, result, handler);
                    }
                }).onFailure(new FailureHandler<ErrorResultException>() {
                    @Override
                    public void handleError(final ErrorResultException error) {
                        if (!(error instanceof EntryNotFoundException)) {
                            handler.handleError(asResourceException(error));
                        } else {
@@ -367,11 +356,6 @@
                            handler.handleResult(null);
                        }
                    }
                    @Override
                    public void handleResult(final SearchResultEntry result) {
                        mapper.read(c, path, result, handler);
                    }
                });
    }