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

Matthew Swift
01.57.2013 16869da731710f4c3754c7b229fa7346211a25a4
Pull EntryContainer into LDAPCollectionResourceProvider until layering is better understood.
1 files deleted
6 files modified
774 ■■■■■ changed files
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java 8 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java 199 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java 32 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java 238 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java 247 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java 48 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -91,10 +91,10 @@
                                    if (f == null) {
                                        // No mapping so remove.
                                        i.remove();
                                    } else if (f == c.getConfig().getFalseFilter()) {
                                        return c.getConfig().getFalseFilter();
                                    } else if (f == c.getConfig().getTrueFilter()) {
                                        return c.getConfig().getTrueFilter();
                                    } else if (f == c.getConfig().falseFilter()) {
                                        return c.getConfig().falseFilter();
                                    } else if (f == c.getConfig().trueFilter()) {
                                        return c.getConfig().trueFilter();
                                    }
                                }
                                switch (value.size()) {
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
@@ -15,6 +15,9 @@
 */
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.opendj.rest2ldap.Config.ReadOnUpdatePolicy.USE_READ_ENTRY_CONTROLS;
import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
import org.forgerock.opendj.ldap.Filter;
/**
@@ -22,24 +25,200 @@
 */
public final class Config {
    private final Filter trueFilter = Filter.objectClassPresent();
    private final Filter falseFilter = Filter.present("1.1");
    /**
     * Returns the absolute true filter.
     *
     * @return The absolute true filter.
     * An interface for incrementally constructing common configuration options.
     */
    public Filter getTrueFilter() {
        return trueFilter;
    public static final class Builder {
        private Filter falseFilter;
        private ReadOnUpdatePolicy readOnUpdatePolicy;
        private Filter trueFilter;
        private Builder() {
            // Nothing to do.
    }
    /**
     * Returns the absolute false filter.
         * Returns a new configuration based on the current state of this
         * builder.
         *
         * @return A new configuration based on the current state of this
         *         builder.
         */
        public Config build() {
            return new Config(trueFilter, falseFilter, readOnUpdatePolicy);
        }
        /**
         * Sets the absolute false filter which should be used when querying the
         * LDAP server.
         *
         * @param filter
         *            The absolute false filter.
         * @return A reference to this builder.
         */
        public Builder falseFilter(final Filter filter) {
            this.trueFilter = ensureNotNull(filter);
            return this;
        }
        /**
         * Sets the policy which should be used in order to read an entry before
         * it is deleted, or after it is added or modified.
         *
         * @param policy
         *            The policy which should be used in order to read an entry
         *            before it is deleted, or after it is added or modified.
         * @return A reference to this builder.
         */
        public Builder readOnUpdatePolicy(final ReadOnUpdatePolicy policy) {
            this.readOnUpdatePolicy = ensureNotNull(policy);
            return this;
        }
        /**
         * Sets the absolute true filter which should be used when querying the
         * LDAP server.
         *
         * @param filter
         *            The absolute true filter.
         * @return A reference to this builder.
         */
        public Builder trueFilter(final Filter filter) {
            this.trueFilter = ensureNotNull(filter);
            return this;
        }
    };
    /**
     * The policy which should be used in order to read an entry before it is
     * deleted, or after it is added or modified.
     */
    public static enum ReadOnUpdatePolicy {
        /**
         * The LDAP entry will not be read when an update is performed. More
         * specifically, the REST resource will not be returned as part of a
         * create, delete, patch, or update request.
         */
        DISABLED,
        /**
         * The LDAP entry will be read atomically using the RFC 4527 read-entry
         * controls. More specifically, the REST resource will be returned as
         * part of a create, delete, patch, or update request, and it will
         * reflect the state of the resource at the time the update was
         * performed. This policy requires that the LDAP server supports RFC
         * 4527.
         */
        USE_READ_ENTRY_CONTROLS,
        /**
         * The LDAP entry will be read non-atomically using an LDAP search when
         * an update is performed. More specifically, the REST resource will be
         * returned as part of a create, delete, patch, or update request, but
         * it may not reflect the state of the resource at the time the update
         * was performed.
         */
        USE_SEARCH;
    }
    private static final Config DEFAULT = new Builder().trueFilter(Filter.objectClassPresent())
            .falseFilter(Filter.present("1.1")).readOnUpdatePolicy(USE_READ_ENTRY_CONTROLS).build();
    /**
     * Returns a new builder which can be used for incrementally constructing
     * common configuration options. The builder will initially have
     * {@link #defaultConfig() default} settings.
     *
     * @return The new builder.
     */
    public static Builder builder() {
        return builder(DEFAULT);
    }
    /**
     * Returns a new builder which can be used for incrementally constructing
     * common configuration options. The builder will initially have the same
     * settings as the provided configuration.
     *
     * @param config
     *            The initial settings.
     * @return The new builder.
     */
    public static Builder builder(final Config config) {
        return new Builder().trueFilter(config.trueFilter()).falseFilter(config.falseFilter())
                .readOnUpdatePolicy(config.readOnUpdatePolicy());
    }
    /**
     * Returns the default configuration having the following settings:
     * <ul>
     * <li>the absolute true filter {@code (objectClass=*)}
     * <li>the absolute false filter {@code (1.1=*)}
     * <li>the read on update policy
     * {@link ReadOnUpdatePolicy#USE_READ_ENTRY_CONTROLS}.
     * </ul>
     *
     * @return The default configuration.
     */
    public static Config defaultConfig() {
        return DEFAULT;
    }
    private final Filter falseFilter;
    private final ReadOnUpdatePolicy readOnUpdatePolicy;
    private final Filter trueFilter;
    private Config(final Filter trueFilter, final Filter falseFilter,
            final ReadOnUpdatePolicy readOnUpdatePolicy) {
        this.trueFilter = trueFilter;
        this.falseFilter = falseFilter;
        this.readOnUpdatePolicy = readOnUpdatePolicy;
    }
    /**
     * Returns the absolute false filter which should be used when querying the
     * LDAP server.
     *
     * @return The absolute false filter.
     */
    public Filter getFalseFilter() {
    public Filter falseFilter() {
        return falseFilter;
    }
    /**
     * Returns the policy which should be used in order to read an entry before
     * it is deleted, or after it is added or modified.
     *
     * @return The policy which should be used in order to read an entry before
     *         it is deleted, or after it is added or modified.
     */
    public ReadOnUpdatePolicy readOnUpdatePolicy() {
        return readOnUpdatePolicy;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("trueFilter=");
        builder.append(trueFilter);
        builder.append(", falseFilter=");
        builder.append(falseFilter);
        builder.append(", readOnUpdatePolicy=");
        builder.append(readOnUpdatePolicy);
        return builder.toString();
    }
    /**
     * Returns the absolute true filter which should be used when querying the
     * LDAP server.
     *
     * @return The absolute true filter.
     */
    public Filter trueFilter() {
        return trueFilter;
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
@@ -69,20 +69,20 @@
        if (jsonAttribute.size() == 1 && jsonAttribute.get(0).equalsIgnoreCase(jsonAttributeName)) {
            final Filter filter;
            if (type == FilterType.PRESENT) {
                filter = c.getConfig().getTrueFilter();
                filter = c.getConfig().trueFilter();
            } else if (jsonAttributeValue instanceof String && valueAssertion instanceof String) {
                final String v1 = toLowerCase((String) jsonAttributeValue);
                final String v2 = toLowerCase((String) valueAssertion);
                switch (type) {
                case CONTAINS:
                    filter =
                            v1.contains(v2) ? c.getConfig().getTrueFilter() : c.getConfig()
                                    .getFalseFilter();
                            v1.contains(v2) ? c.getConfig().trueFilter() : c.getConfig()
                                    .falseFilter();
                    break;
                case STARTS_WITH:
                    filter =
                            v1.startsWith(v2) ? c.getConfig().getTrueFilter() : c.getConfig()
                                    .getFalseFilter();
                            v1.startsWith(v2) ? c.getConfig().trueFilter() : c.getConfig()
                                    .falseFilter();
                    break;
                default:
                    filter = compare(c, type, v1, v2);
@@ -98,7 +98,7 @@
                filter = compare(c, type, v1, v2);
            } else {
                // This attribute mapper is a candidate but it does not match.
                filter = c.getConfig().getFalseFilter();
                filter = c.getConfig().falseFilter();
            }
            h.handleResult(filter);
        } else {
@@ -130,30 +130,30 @@
        final Filter filter;
        switch (type) {
        case EQUAL_TO:
            filter = v1.equals(v2) ? c.getConfig().getTrueFilter() : c.getConfig().getFalseFilter();
            filter = v1.equals(v2) ? c.getConfig().trueFilter() : c.getConfig().falseFilter();
            break;
        case GREATER_THAN:
            filter =
                    v1.compareTo(v2) > 0 ? c.getConfig().getTrueFilter() : c.getConfig()
                            .getFalseFilter();
                    v1.compareTo(v2) > 0 ? c.getConfig().trueFilter() : c.getConfig()
                            .falseFilter();
            break;
        case GREATER_THAN_OR_EQUAL_TO:
            filter =
                    v1.compareTo(v2) >= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
                            .getFalseFilter();
                    v1.compareTo(v2) >= 0 ? c.getConfig().trueFilter() : c.getConfig()
                            .falseFilter();
            break;
        case LESS_THAN:
            filter =
                    v1.compareTo(v2) < 0 ? c.getConfig().getTrueFilter() : c.getConfig()
                            .getFalseFilter();
                    v1.compareTo(v2) < 0 ? c.getConfig().trueFilter() : c.getConfig()
                            .falseFilter();
            break;
        case LESS_THAN_OR_EQUAL_TO:
            filter =
                    v1.compareTo(v2) <= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
                            .getFalseFilter();
                    v1.compareTo(v2) <= 0 ? c.getConfig().trueFilter() : c.getConfig()
                            .falseFilter();
            break;
        default:
            filter = c.getConfig().getFalseFilter(); // Not supported.
            filter = c.getConfig().falseFilter(); // Not supported.
            break;
        }
        return filter;
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
File was deleted
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -51,14 +51,21 @@
import org.forgerock.opendj.ldap.AssertionFailureException;
import org.forgerock.opendj.ldap.AuthenticationException;
import org.forgerock.opendj.ldap.AuthorizationException;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionException;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.EntryNotFoundException;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Function;
import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.TimeoutResultException;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
@@ -68,25 +75,120 @@
 * resource collection to LDAP entries beneath a base DN.
 */
public class LDAPCollectionResourceProvider implements CollectionResourceProvider {
    private abstract class AbstractRequestCompletionHandler<R,
            H extends org.forgerock.opendj.ldap.ResultHandler<? super R>>
            implements org.forgerock.opendj.ldap.ResultHandler<R> {
        final Connection connection;
        final H resultHandler;
        AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
            this.connection = connection;
            this.resultHandler = resultHandler;
        }
        @Override
        public final void handleErrorResult(final ErrorResultException error) {
            connection.close();
            resultHandler.handleErrorResult(error);
        }
        @Override
        public final void handleResult(final R result) {
            connection.close();
            resultHandler.handleResult(result);
        }
    }
    private abstract class ConnectionCompletionHandler<R> implements
            org.forgerock.opendj.ldap.ResultHandler<Connection> {
        private final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler;
        ConnectionCompletionHandler(
                final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler) {
            this.resultHandler = resultHandler;
        }
        @Override
        public final void handleErrorResult(final ErrorResultException error) {
            resultHandler.handleErrorResult(error);
        }
        @Override
        public abstract void handleResult(Connection connection);
    }
    private final class RequestCompletionHandler<R> extends
            AbstractRequestCompletionHandler<R, org.forgerock.opendj.ldap.ResultHandler<? super R>> {
        RequestCompletionHandler(final Connection connection,
                final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler) {
            super(connection, resultHandler);
        }
    }
    private final class SearchRequestCompletionHandler extends
            AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
            SearchResultHandler {
        SearchRequestCompletionHandler(final Connection connection,
                final SearchResultHandler resultHandler) {
            super(connection, resultHandler);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public final boolean handleEntry(final SearchResultEntry entry) {
            return resultHandler.handleEntry(entry);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public final boolean handleReference(final SearchResultReference reference) {
            return resultHandler.handleReference(reference);
        }
    }
    // FIXME: make this configurable.
    private static final String ETAG_ATTRIBUTE = "etag";
    // Dummy exception used for signalling search success.
    private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
    // FIXME: make this configurable, also allow use of DN.
    private static final String UUID_ATTRIBUTE = "entryUUID";
    private final AttributeMapper attributeMapper;
    private final EntryContainer entryContainer;
    private final Config config = new Config();
    private final DN baseDN; // TODO: support template variables.
    private final Config config;
    private final ConnectionFactory factory;
    /**
     * Creates a new LDAP resource.
     *
     * @param container
     *            The LDAP entry container.
     * @param baseDN
     *            The parent of all entries contained in this LDAP collection.
     * @param mapper
     *            The attribute mapper which will be used for mapping LDAP
     *            attributes to JSON attributes.
     * @param factory
     *            The LDAP connection factory which will be used for performing
     *            LDAP operations.
     * @param config
     *            Common configuration options.
     */
    public LDAPCollectionResourceProvider(final EntryContainer container,
            final AttributeMapper mapper) {
        this.entryContainer = container;
    public LDAPCollectionResourceProvider(final DN baseDN, final AttributeMapper mapper,
            final ConnectionFactory factory, final Config config) {
        this.baseDN = baseDN;
        this.attributeMapper = mapper;
        this.factory = factory;
        this.config = config;
    }
    /**
@@ -140,8 +242,10 @@
    @Override
    public void queryCollection(final ServerContext context, final QueryRequest request,
            final QueryResultHandler handler) {
        // List the entries.
        final Context c = wrap(context);
        final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
        // The handler which will be invoked for each LDAP search result.
        final SearchResultHandler searchHandler = new SearchResultHandler() {
            private final AtomicInteger pendingResourceCount = new AtomicInteger();
            private final AtomicReference<ResourceException> pendingResult =
@@ -161,8 +265,8 @@
                // TODO: should the resource or the container define the ID
                // mapping?
                final String id = entryContainer.getIDFromEntry(entry);
                final String revision = entryContainer.getEtagFromEntry(entry);
                final String id = getIDFromEntry(entry);
                final String revision = getEtagFromEntry(entry);
                final ResultHandler<Map<String, Object>> mapHandler =
                        new ResultHandler<Map<String, Object>>() {
                            @Override
@@ -224,8 +328,8 @@
            }
        };
        final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
        getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() {
        // The handler which will be invoked once the LDAP filter has been transformed.
        final ResultHandler<Filter> filterHandler = new ResultHandler<Filter>() {
            @Override
            public void handleError(final ResourceException error) {
                handler.handleError(error);
@@ -234,13 +338,32 @@
            @Override
            public void handleResult(final Filter ldapFilter) {
                // Avoid performing a search if the filter could not be mapped or if it will never match.
                if (ldapFilter == null || ldapFilter == c.getConfig().getFalseFilter()) {
                if (ldapFilter == null || ldapFilter == c.getConfig().falseFilter()) {
                    handler.handleResult(new QueryResult());
                } else {
                    entryContainer.listEntries(c, ldapFilter, ldapAttributes, searchHandler);
                    final String[] tmp = getSearchAttributes(ldapAttributes);
                    final ConnectionCompletionHandler<Result> outerHandler =
                            new ConnectionCompletionHandler<Result>(searchHandler) {
                                @Override
                                public void handleResult(final Connection connection) {
                                    final SearchRequestCompletionHandler innerHandler =
                                            new SearchRequestCompletionHandler(connection,
                                                    searchHandler);
                                    final SearchRequest request =
                                            Requests.newSearchRequest(baseDN,
                                                    SearchScope.SINGLE_LEVEL, ldapFilter, tmp);
                                    connection.searchAsync(request, null, innerHandler);
                                }
                            };
                    factory.getConnectionAsync(outerHandler);
                }
            }
        });
        };
        getLDAPFilter(c, request.getQueryFilter(), filterHandler);
    }
    /**
@@ -250,7 +373,10 @@
    public void readInstance(final ServerContext context, final String resourceId,
            final ReadRequest request, final ResultHandler<Resource> handler) {
        final Context c = wrap(context);
        // @Checkstyle:off
        final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
        final String[] tmp = getSearchAttributes(ldapAttributes);
        // The handler which will be invoked for the LDAP search result.
        final org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler =
                new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
                    @Override
@@ -260,7 +386,7 @@
                    @Override
                    public void handleResult(final SearchResultEntry entry) {
                        final String revision = entryContainer.getEtagFromEntry(entry);
                        final String revision = getEtagFromEntry(entry);
                        final ResultHandler<Map<String, Object>> mapHandler =
                                new ResultHandler<Map<String, Object>>() {
                                    @Override
@@ -279,9 +405,25 @@
                        attributeMapper.toJSON(c, entry, mapHandler);
                    }
                };
        // @Checkstyle:on
        final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
        entryContainer.readEntry(c, resourceId, ldapAttributes, searchHandler);
        // The handler which will be invoked
        final ConnectionCompletionHandler<SearchResultEntry> outerHandler =
                new ConnectionCompletionHandler<SearchResultEntry>(searchHandler) {
                    @Override
                    public void handleResult(final Connection connection) {
                        final RequestCompletionHandler<SearchResultEntry> innerHandler =
                                new RequestCompletionHandler<SearchResultEntry>(connection,
                                        searchHandler);
                        final SearchRequest request =
                                Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
                                        .equality(UUID_ATTRIBUTE, resourceId), tmp);
                        connection.searchSingleEntryAsync(request, innerHandler);
                    }
                };
        factory.getConnectionAsync(outerHandler);
    }
    /**
@@ -325,6 +467,28 @@
    }
    /**
     * Returns the ETag for the provided entry.
     *
     * @param entry
     *            The entry.
     * @return The ETag.
     */
    private String getEtagFromEntry(final Entry entry) {
        return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
    }
    /**
     * Returns the resource ID for the provided entry.
     *
     * @param entry
     *            The entry.
     * @return The resource ID.
     */
    private String getIDFromEntry(final Entry entry) {
        return entry.parseAttribute(UUID_ATTRIBUTE).asString();
    }
    /**
     * Determines the set of LDAP attributes to request in an LDAP read (search,
     * post-read), based on the provided list of JSON pointers.
     *
@@ -369,16 +533,16 @@
                                                    final Filter f = i.next();
                                                    if (f == null) {
                                                        // Filter component did not match any attribute mappers.
                                                        return c.getConfig().getFalseFilter();
                                                    } else if (f == c.getConfig().getFalseFilter()) {
                                                        return c.getConfig().getFalseFilter();
                                                    } else if (f == c.getConfig().getTrueFilter()) {
                                                        return c.getConfig().falseFilter();
                                                    } else if (f == c.getConfig().falseFilter()) {
                                                        return c.getConfig().falseFilter();
                                                    } else if (f == c.getConfig().trueFilter()) {
                                                        i.remove();
                                                    }
                                                }
                                                switch (value.size()) {
                                                case 0:
                                                    return c.getConfig().getTrueFilter();
                                                    return c.getConfig().trueFilter();
                                                case 1:
                                                    return value.get(0);
                                                default:
@@ -395,8 +559,8 @@
                    @Override
                    public Void visitBooleanLiteralFilter(final ResultHandler<Filter> p,
                            final boolean value) {
                        p.handleResult(value ? c.getConfig().getTrueFilter() : c.getConfig()
                                .getFalseFilter());
                        p.handleResult(value ? c.getConfig().trueFilter() : c.getConfig()
                                .falseFilter());
                        return null;
                    }
@@ -465,11 +629,11 @@
                            public Filter apply(final Filter value, final Void p) {
                                if (value == null) {
                                    // Filter component did not match any attribute mappers.
                                    return c.getConfig().getTrueFilter();
                                } else if (value == c.getConfig().getFalseFilter()) {
                                    return c.getConfig().getTrueFilter();
                                } else if (value == c.getConfig().getTrueFilter()) {
                                    return c.getConfig().getFalseFilter();
                                    return c.getConfig().trueFilter();
                                } else if (value == c.getConfig().falseFilter()) {
                                    return c.getConfig().trueFilter();
                                } else if (value == c.getConfig().trueFilter()) {
                                    return c.getConfig().falseFilter();
                                } else {
                                    return Filter.not(value);
                                }
@@ -494,15 +658,15 @@
                                                    if (f == null) {
                                                        // Filter component did not match any attribute mappers.
                                                        i.remove();
                                                    } else if (f == c.getConfig().getFalseFilter()) {
                                                    } else if (f == c.getConfig().falseFilter()) {
                                                        i.remove();
                                                    } else if (f == c.getConfig().getTrueFilter()) {
                                                        return c.getConfig().getTrueFilter();
                                                    } else if (f == c.getConfig().trueFilter()) {
                                                        return c.getConfig().trueFilter();
                                                    }
                                                }
                                                switch (value.size()) {
                                                case 0:
                                                    return c.getConfig().getFalseFilter();
                                                    return c.getConfig().falseFilter();
                                                case 1:
                                                    return value.get(0);
                                                default:
@@ -536,7 +700,16 @@
        queryFilter.accept(visitor, h);
    }
    private Context wrap(ServerContext context) {
    private String[] getSearchAttributes(final Collection<String> attributes) {
        // FIXME: who is responsible for adding the UUID and etag attributes to
        // this search?
        final String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
        tmp[tmp.length - 2] = UUID_ATTRIBUTE;
        tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
        return tmp;
    }
    private Context wrap(final ServerContext context) {
        return new Context(config, context);
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -173,7 +173,7 @@
            break;
        case EXTENDED:
        default:
            filter = c.getConfig().getFalseFilter(); // Not supported.
            filter = c.getConfig().falseFilter(); // Not supported.
            break;
        }
        return filter;
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -50,50 +50,52 @@
     */
    public static void main(final String[] args) throws Exception {
        // All LDAP resources will use this connection factory.
        final ConnectionFactory ldapFactory = newAuthenticatedConnectionFactory(
                new LDAPConnectionFactory("localhost", 1389), Requests.newSimpleBindRequest(
                        "cn=directory manager", "password".toCharArray()));
        // Create two entry containers whose members reference each other.
        final EntryContainer userContainer = new EntryContainer(DN
                .valueOf("ou=people,dc=example,dc=com"), ldapFactory);
        final EntryContainer groupContainer = new EntryContainer(DN
                .valueOf("ou=groups,dc=example,dc=com"), ldapFactory);
        final ConnectionFactory ldapFactory =
                newAuthenticatedConnectionFactory(new LDAPConnectionFactory("localhost", 1389),
                        Requests.newSimpleBindRequest("cn=directory manager", "password"
                                .toCharArray()));
        // Create user resource.
        final AttributeMapper userMapper = new CompositeAttributeMapper().addMapper(
        final AttributeMapper userMapper =
                new CompositeAttributeMapper().addMapper(
                new SimpleAttributeMapper("id", "entryUUID").singleValued(true)).addMapper(
                new DefaultAttributeMapper().includeAttribute("uid", "isMemberOf",
                        "modifyTimestamp")).addMapper(
                new ComplexAttributeMapper("name", new DefaultAttributeMapper().includeAttribute(
                        "cn", "sn", "givenName"))).addMapper(
                new ComplexAttributeMapper("contactInformation", new CompositeAttributeMapper()
                        .addMapper(
                        new ComplexAttributeMapper("name", new DefaultAttributeMapper()
                                .includeAttribute("cn", "sn", "givenName"))).addMapper(
                        new ComplexAttributeMapper("contactInformation",
                                new CompositeAttributeMapper().addMapper(
                                new SimpleAttributeMapper("telephoneNumber").decoder(
                                        Functions.byteStringToString()).singleValued(true))
                        .addMapper(
                                new SimpleAttributeMapper("emailAddress", "mail")
                                        .singleValued(true))));
        final LDAPCollectionResourceProvider userResource = new LDAPCollectionResourceProvider(
                userContainer, userMapper);
        final LDAPCollectionResourceProvider userResource =
                new LDAPCollectionResourceProvider(DN.valueOf("ou=people,dc=example,dc=com"),
                        userMapper, ldapFactory, Config.defaultConfig());
        // Create group resource.
        final AttributeMapper groupMapper = new DefaultAttributeMapper().includeAttribute("cn",
                "ou", "description", "uniquemember");
        final LDAPCollectionResourceProvider groupResource = new LDAPCollectionResourceProvider(
                groupContainer, groupMapper);
        final AttributeMapper groupMapper =
                new DefaultAttributeMapper().includeAttribute("cn", "ou", "description",
                        "uniquemember");
        final LDAPCollectionResourceProvider groupResource =
                new LDAPCollectionResourceProvider(DN.valueOf("ou=groups,dc=example,dc=com"),
                        groupMapper, ldapFactory, Config.defaultConfig());
        // Create the router.
        final Router router = new Router();
        router.addRoute("/users", userResource);
        router.addRoute("/groups", groupResource);
        final org.forgerock.json.resource.ConnectionFactory resourceFactory = newInternalConnectionFactory(router);
        final org.forgerock.json.resource.ConnectionFactory resourceFactory =
                newInternalConnectionFactory(router);
        final HttpServer httpServer = HttpServer.createSimpleServer("./", PORT);
        try {
            final WebappContext ctx = new WebappContext("example", "/example");
            final ServletRegistration reg = ctx.addServlet("managed", new HttpServlet(
                    resourceFactory));
            final ServletRegistration reg =
                    ctx.addServlet("managed", new HttpServlet(resourceFactory));
            reg.addMapping("/managed/*");
            ctx.deploy(httpServer);