001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027package org.forgerock.opendj.config;
028
029import java.util.Arrays;
030import java.util.HashSet;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.MissingResourceException;
034import java.util.NoSuchElementException;
035import java.util.Set;
036
037/**
038 * This class is used to map configuration elements to their LDAP schema names.
039 * <p>
040 * It is possible to augment the core LDAP profile with additional profile
041 * mappings at run-time using instances of {@link Wrapper}. This is useful for
042 * unit tests which need to add and remove mock components.
043 */
044public final class LDAPProfile {
045
046    /**
047     * LDAP profile wrappers can be used to provide temporary LDAP profile
048     * information for components which do not have LDAP profile property files.
049     * These components are typically "mock" components used in unit-tests.
050     */
051    public static abstract class Wrapper {
052
053        /** Default constructor. */
054        protected Wrapper() {
055            // No implementation required.
056        }
057
058        /**
059         * Get the name of the LDAP attribute associated with the specified
060         * property definition.
061         * <p>
062         * The default implementation of this method is to return
063         * <code>null</code>.
064         *
065         * @param d
066         *            The managed object definition.
067         * @param pd
068         *            The property definition.
069         * @return Returns the name of the LDAP attribute associated with the
070         *         specified property definition, or <code>null</code> if the
071         *         property definition is not handled by this LDAP profile
072         *         wrapper.
073         */
074        public String getAttributeName(AbstractManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd) {
075            return null;
076        }
077
078        /**
079         * Gets the LDAP RDN attribute type for child entries of an instantiable
080         * relation.
081         * <p>
082         * The default implementation of this method is to return
083         * <code>null</code>.
084         *
085         * @param r
086         *            The instantiable relation.
087         * @return Returns the LDAP RDN attribute type for child entries of an
088         *         instantiable relation, or <code>null</code> if the
089         *         instantiable relation is not handled by this LDAP profile
090         *         wrapper.
091         */
092        public String getRelationChildRDNType(InstantiableRelationDefinition<?, ?> r) {
093            return null;
094        }
095
096        /**
097         * Gets the LDAP RDN attribute type for child entries of an set
098         * relation.
099         * <p>
100         * The default implementation of this method is to return
101         * <code>null</code>.
102         *
103         * @param r
104         *            The set relation.
105         * @return Returns the LDAP RDN attribute type for child entries of an
106         *         set relation, or <code>null</code> if the set relation is not
107         *         handled by this LDAP profile wrapper.
108         */
109        public String getRelationChildRDNType(SetRelationDefinition<?, ?> r) {
110            return null;
111        }
112
113        /**
114         * Get the principle object class associated with the specified
115         * definition.
116         * <p>
117         * The default implementation of this method is to return
118         * <code>null</code>.
119         *
120         * @param d
121         *            The managed object definition.
122         * @return Returns the principle object class associated with the
123         *         specified definition, or <code>null</code> if the managed
124         *         object definition is not handled by this LDAP profile
125         *         wrapper.
126         */
127        public String getObjectClass(AbstractManagedObjectDefinition<?, ?> d) {
128            return null;
129        }
130
131        /**
132         * Get an LDAP RDN sequence associatied with a relation.
133         * <p>
134         * The default implementation of this method is to return
135         * <code>null</code>.
136         *
137         * @param r
138         *            The relation.
139         * @return Returns the LDAP RDN sequence associatied with a relation, or
140         *         <code>null</code> if the relation is not handled by this LDAP
141         *         profile wrapper.
142         */
143        public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
144            return null;
145        }
146    }
147
148    /** The singleton instance. */
149    private static final LDAPProfile INSTANCE = new LDAPProfile();
150
151    /**
152     * Get the global LDAP profile instance.
153     *
154     * @return Returns the global LDAP profile instance.
155     */
156    public static LDAPProfile getInstance() {
157        return INSTANCE;
158    }
159
160    /** The list of profile wrappers. */
161    private final LinkedList<Wrapper> profiles = new LinkedList<>();
162
163    /** The LDAP profile property table. */
164    private final ManagedObjectDefinitionResource resource = ManagedObjectDefinitionResource.createForProfile("ldap");
165
166    /** Prevent construction. */
167    private LDAPProfile() {
168        // No implementation required.
169    }
170
171    /**
172     * Get the name of the LDAP attribute associated with the specified property
173     * definition.
174     *
175     * @param d
176     *            The managed object definition.
177     * @param pd
178     *            The property definition.
179     * @return Returns the name of the LDAP attribute associated with the
180     *         specified property definition.
181     * @throws MissingResourceException
182     *             If the LDAP profile properties file associated with the
183     *             provided managed object definition could not be loaded.
184     */
185    public String getAttributeName(AbstractManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd) {
186        for (Wrapper profile : profiles) {
187            String attributeName = profile.getAttributeName(d, pd);
188            if (attributeName != null) {
189                return attributeName;
190            }
191        }
192        return resource.getString(d, "attribute." + pd.getName());
193    }
194
195    /**
196     * Gets the LDAP RDN attribute type for child entries of an instantiable
197     * relation.
198     *
199     * @param r
200     *            The instantiable relation.
201     * @return Returns the LDAP RDN attribute type for child entries of an
202     *         instantiable relation.
203     * @throws MissingResourceException
204     *             If the LDAP profile properties file associated with the
205     *             provided managed object definition could not be loaded.
206     */
207    public String getRelationChildRDNType(InstantiableRelationDefinition<?, ?> r) {
208        if (r.getNamingPropertyDefinition() != null) {
209            // Use the attribute associated with the naming property.
210            return getAttributeName(r.getChildDefinition(), r.getNamingPropertyDefinition());
211        } else {
212            for (Wrapper profile : profiles) {
213                String rdnType = profile.getRelationChildRDNType(r);
214                if (rdnType != null) {
215                    return rdnType;
216                }
217            }
218            return resource.getString(r.getParentDefinition(), "naming-attribute." + r.getName());
219        }
220    }
221
222    /**
223     * Gets the LDAP object classes associated with an instantiable or set
224     * relation branch. The branch is the parent entry of child managed objects.
225     *
226     * @param r
227     *            The instantiable or set relation.
228     * @return Returns the LDAP object classes associated with an instantiable
229     *         or set relation branch.
230     */
231    public List<String> getRelationObjectClasses(RelationDefinition<?, ?> r) {
232        return Arrays.asList(new String[] { "top", "ds-cfg-branch" });
233    }
234
235    /**
236     * Gets the LDAP RDN attribute type for child entries of an set relation.
237     *
238     * @param r
239     *            The set relation.
240     * @return Returns the LDAP RDN attribute type for child entries of an set
241     *         relation.
242     * @throws MissingResourceException
243     *             If the LDAP profile properties file associated with the
244     *             provided managed object definition could not be loaded.
245     */
246    public String getRelationChildRDNType(SetRelationDefinition<?, ?> r) {
247        for (Wrapper profile : profiles) {
248            String rdnType = profile.getRelationChildRDNType(r);
249            if (rdnType != null) {
250                return rdnType;
251            }
252        }
253        return resource.getString(r.getParentDefinition(), "naming-attribute." + r.getName());
254    }
255
256    /**
257     * Get the principle object class associated with the specified definition.
258     *
259     * @param d
260     *            The managed object definition.
261     * @return Returns the principle object class associated with the specified
262     *         definition.
263     * @throws MissingResourceException
264     *             If the LDAP profile properties file associated with the
265     *             provided managed object definition could not be loaded.
266     */
267    public String getObjectClass(AbstractManagedObjectDefinition<?, ?> d) {
268        if (d.isTop()) {
269            return "top";
270        }
271
272        for (Wrapper profile : profiles) {
273            String objectClass = profile.getObjectClass(d);
274            if (objectClass != null) {
275                return objectClass;
276            }
277        }
278        return resource.getString(d, "objectclass");
279    }
280
281    /**
282     * Get all the object classes associated with the specified definition.
283     * <p>
284     * The returned list is ordered such that the uppermost object classes
285     * appear first (e.g. top).
286     *
287     * @param d
288     *            The managed object definition.
289     * @return Returns all the object classes associated with the specified
290     *         definition.
291     * @throws MissingResourceException
292     *             If the LDAP profile properties file associated with the
293     *             provided managed object definition could not be loaded.
294     */
295    public List<String> getObjectClasses(AbstractManagedObjectDefinition<?, ?> d) {
296        LinkedList<String> objectClasses = new LinkedList<>();
297        Set<String> s = new HashSet<>();
298
299        // Add the object classes from the parent hierarchy.
300        while (d != null) {
301            String oc = getObjectClass(d);
302            if (s.add(oc)) {
303                objectClasses.addFirst(oc);
304            }
305            d = d.getParent();
306        }
307
308        if (!s.contains("top")) {
309            objectClasses.addFirst("top");
310        }
311
312        return objectClasses;
313    }
314
315    /**
316     * Get an LDAP RDN sequence associated with a relation.
317     *
318     * @param r
319     *            The relation.
320     * @return Returns the LDAP RDN sequence associated with a relation.
321     * @throws MissingResourceException
322     *             If the LDAP profile properties file associated with the
323     *             provided managed object definition could not be loaded.
324     */
325    public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
326        for (Wrapper profile : profiles) {
327            String rdnSequence = profile.getRelationRDNSequence(r);
328            if (rdnSequence != null) {
329                return rdnSequence;
330            }
331        }
332        return resource.getString(r.getParentDefinition(), "rdn." + r.getName());
333    }
334
335    /**
336     * Removes the last LDAP profile wrapper added using
337     * {@link #pushWrapper(org.forgerock.opendj.config.LDAPProfile.Wrapper)}.
338     *
339     * @throws NoSuchElementException
340     *             If there are no LDAP profile wrappers.
341     */
342    public void popWrapper() {
343        profiles.removeFirst();
344    }
345
346    /**
347     * Decorates the core LDAP profile with the provided LDAP profile wrapper.
348     * All profile requests will be directed to the provided wrapper before
349     * being forwarded onto the core profile if the request could not be
350     * satisfied.
351     *
352     * @param wrapper
353     *            The LDAP profile wrapper.
354     */
355    public void pushWrapper(Wrapper wrapper) {
356        profiles.addFirst(wrapper);
357    }
358}