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