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 2014-2015 ForgeRock AS
026 */
027package org.forgerock.opendj.config.client.spi;
028
029import java.util.Collection;
030import java.util.Collections;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Set;
034import java.util.SortedSet;
035import java.util.TreeSet;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
039import org.forgerock.opendj.config.Configuration;
040import org.forgerock.opendj.config.ConfigurationClient;
041import org.forgerock.opendj.config.Constraint;
042import org.forgerock.opendj.config.PropertyException;
043import org.forgerock.opendj.config.DefaultManagedObject;
044import org.forgerock.opendj.config.DefinitionDecodingException;
045import org.forgerock.opendj.config.InstantiableRelationDefinition;
046import org.forgerock.opendj.config.ManagedObjectAlreadyExistsException;
047import org.forgerock.opendj.config.ManagedObjectDefinition;
048import org.forgerock.opendj.config.ManagedObjectNotFoundException;
049import org.forgerock.opendj.config.ManagedObjectPath;
050import org.forgerock.opendj.config.OptionalRelationDefinition;
051import org.forgerock.opendj.config.PropertyDefinition;
052import org.forgerock.opendj.config.PropertyOption;
053import org.forgerock.opendj.config.RelationDefinition;
054import org.forgerock.opendj.config.RelationDefinitionVisitor;
055import org.forgerock.opendj.config.SetRelationDefinition;
056import org.forgerock.opendj.config.SingletonRelationDefinition;
057import org.forgerock.opendj.config.DefinitionDecodingException.Reason;
058import org.forgerock.opendj.config.client.ClientConstraintHandler;
059import org.forgerock.opendj.config.client.ConcurrentModificationException;
060import org.forgerock.opendj.config.client.IllegalManagedObjectNameException;
061import org.forgerock.opendj.config.client.ManagedObject;
062import org.forgerock.opendj.config.client.ManagedObjectDecodingException;
063import org.forgerock.opendj.config.client.ManagementContext;
064import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException;
065import org.forgerock.opendj.config.client.OperationRejectedException;
066import org.forgerock.opendj.config.client.OperationRejectedException.OperationType;
067import org.forgerock.opendj.ldap.LdapException;
068
069/**
070 * An abstract managed object implementation.
071 *
072 * @param <T>
073 *            The type of client configuration represented by the client managed
074 *            object.
075 */
076public abstract class AbstractManagedObject<T extends ConfigurationClient> implements ManagedObject<T> {
077
078    /**
079     * Creates any default managed objects associated with a relation
080     * definition.
081     */
082    private final class DefaultManagedObjectFactory implements RelationDefinitionVisitor<Void, Void> {
083
084        /** Possible exceptions. */
085        private ManagedObjectAlreadyExistsException moaee;
086        private MissingMandatoryPropertiesException mmpe;
087        private ConcurrentModificationException cme;
088        private OperationRejectedException ore;
089        private LdapException ere;
090
091        /** {@inheritDoc} */
092        @Override
093        public <C extends ConfigurationClient, S extends Configuration> Void visitInstantiable(
094            InstantiableRelationDefinition<C, S> rd, Void p) {
095            for (String name : rd.getDefaultManagedObjectNames()) {
096                DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject(name);
097                ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition();
098                ManagedObject<? extends C> child;
099                try {
100                    child = createChild(rd, d, name, null);
101                } catch (IllegalManagedObjectNameException e) {
102                    // This should not happen.
103                    throw new RuntimeException(e);
104                }
105                createDefaultManagedObject(d, child, dmo);
106            }
107            return null;
108        }
109
110        /** {@inheritDoc} */
111        @Override
112        public <C extends ConfigurationClient, S extends Configuration> Void visitOptional(
113            OptionalRelationDefinition<C, S> rd, Void p) {
114            if (rd.getDefaultManagedObject() != null) {
115                DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject();
116                ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition();
117                ManagedObject<? extends C> child = createChild(rd, d, null);
118                createDefaultManagedObject(d, child, dmo);
119            }
120            return null;
121        }
122
123        /** {@inheritDoc} */
124        @Override
125        public <C extends ConfigurationClient, S extends Configuration> Void visitSingleton(
126            SingletonRelationDefinition<C, S> rd, Void p) {
127            // Do nothing - not possible to create singletons
128            // dynamically.
129            return null;
130        }
131
132        /** {@inheritDoc} */
133        @Override
134        public <C extends ConfigurationClient, S extends Configuration> Void visitSet(SetRelationDefinition<C, S> rd,
135            Void p) {
136            for (String name : rd.getDefaultManagedObjectNames()) {
137                DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject(name);
138                ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition();
139                ManagedObject<? extends C> child = createChild(rd, d, null);
140                createDefaultManagedObject(d, child, dmo);
141            }
142            return null;
143        }
144
145        /** Create the child managed object. */
146        private void createDefaultManagedObject(ManagedObjectDefinition<?, ?> d, ManagedObject<?> child,
147            DefaultManagedObject<?, ?> dmo) {
148            for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
149                setPropertyValues(child, pd, dmo);
150            }
151
152            try {
153                child.commit();
154            } catch (ManagedObjectAlreadyExistsException e) {
155                moaee = e;
156            } catch (MissingMandatoryPropertiesException e) {
157                mmpe = e;
158            } catch (ConcurrentModificationException e) {
159                cme = e;
160            } catch (OperationRejectedException e) {
161                ore = e;
162            } catch (LdapException e) {
163                ere = e;
164            }
165        }
166
167        /**
168         * Creates the default managed objects associated with the provided
169         * relation definition.
170         *
171         * @param rd
172         *            The relation definition.
173         */
174        private void createDefaultManagedObjects(RelationDefinition<?, ?> rd) throws LdapException,
175            ConcurrentModificationException, MissingMandatoryPropertiesException,
176            ManagedObjectAlreadyExistsException, OperationRejectedException {
177            rd.accept(this, null);
178
179            if (ere != null) {
180                throw ere;
181            } else if (cme != null) {
182                throw cme;
183            } else if (mmpe != null) {
184                throw mmpe;
185            } else if (moaee != null) {
186                throw moaee;
187            } else if (ore != null) {
188                throw ore;
189            }
190        }
191
192        /** Set property values. */
193        private <P> void setPropertyValues(ManagedObject<?> mo, PropertyDefinition<P> pd,
194            DefaultManagedObject<?, ?> dmo) {
195            mo.setPropertyValues(pd, dmo.getPropertyValues(pd));
196        }
197    }
198
199    /**
200     * The managed object definition associated with this managed
201     * object.
202     */
203    private final ManagedObjectDefinition<T, ? extends Configuration> definition;
204
205    /**
206     * Indicates whether or not this managed object exists on the server
207     * (false means the managed object is new and has not been
208     * committed).
209     */
210    private boolean existsOnServer;
211
212    /** Optional naming property definition. */
213    private final PropertyDefinition<?> namingPropertyDefinition;
214
215    /** The path associated with this managed object. */
216    private ManagedObjectPath<T, ? extends Configuration> path;
217
218    /** The managed object's properties. */
219    private final PropertySet properties;
220
221    /**
222     * Creates a new abstract managed object.
223     *
224     * @param d
225     *            The managed object's definition.
226     * @param path
227     *            The managed object's path.
228     * @param properties
229     *            The managed object's properties.
230     * @param existsOnServer
231     *            Indicates whether or not the managed object exists on the
232     *            server (false means the managed object is new and has not been
233     *            committed).
234     * @param namingPropertyDefinition
235     *            Optional naming property definition.
236     */
237    protected AbstractManagedObject(ManagedObjectDefinition<T, ? extends Configuration> d,
238        ManagedObjectPath<T, ? extends Configuration> path, PropertySet properties, boolean existsOnServer,
239        PropertyDefinition<?> namingPropertyDefinition) {
240        this.definition = d;
241        this.path = path;
242        this.properties = properties;
243        this.existsOnServer = existsOnServer;
244        this.namingPropertyDefinition = namingPropertyDefinition;
245    }
246
247    /** {@inheritDoc} */
248    @Override
249    public final void commit() throws ManagedObjectAlreadyExistsException, MissingMandatoryPropertiesException,
250        ConcurrentModificationException, OperationRejectedException, LdapException {
251        // First make sure all mandatory properties are defined.
252        List<PropertyException> exceptions = new LinkedList<>();
253
254        for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
255            Property<?> p = getProperty(pd);
256            if (pd.hasOption(PropertyOption.MANDATORY) && p.getEffectiveValues().isEmpty()) {
257                exceptions.add(PropertyException.propertyIsMandatoryException(pd));
258            }
259        }
260
261        if (!exceptions.isEmpty()) {
262            throw new MissingMandatoryPropertiesException(definition.getUserFriendlyName(), exceptions,
263                !existsOnServer);
264        }
265
266        // Now enforce any constraints.
267        List<LocalizableMessage> messages = new LinkedList<>();
268        boolean isAcceptable = true;
269        ManagementContext context = getDriver().getManagementContext();
270
271        for (Constraint constraint : definition.getAllConstraints()) {
272            for (ClientConstraintHandler handler : constraint.getClientConstraintHandlers()) {
273                if (existsOnServer) {
274                    if (!handler.isModifyAcceptable(context, this, messages)) {
275                        isAcceptable = false;
276                    }
277                } else {
278                    if (!handler.isAddAcceptable(context, this, messages)) {
279                        isAcceptable = false;
280                    }
281                }
282            }
283            if (!isAcceptable) {
284                break;
285            }
286        }
287
288        if (!isAcceptable) {
289            if (existsOnServer) {
290                throw new OperationRejectedException(OperationType.MODIFY, definition.getUserFriendlyName(), messages);
291            } else {
292                throw new OperationRejectedException(OperationType.CREATE, definition.getUserFriendlyName(), messages);
293            }
294        }
295
296        // Commit the managed object.
297        if (existsOnServer) {
298            modifyExistingManagedObject();
299        } else {
300            addNewManagedObject();
301        }
302
303        // Make all pending property values active.
304        properties.commit();
305
306        // If the managed object was created make sure that any default
307        // subordinate managed objects are also created.
308        if (!existsOnServer) {
309            DefaultManagedObjectFactory factory = new DefaultManagedObjectFactory();
310            for (RelationDefinition<?, ?> rd : definition.getAllRelationDefinitions()) {
311                factory.createDefaultManagedObjects(rd);
312            }
313
314            existsOnServer = true;
315        }
316    }
317
318    /** {@inheritDoc} */
319    @Override
320    public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild(
321        InstantiableRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d, String name,
322        Collection<PropertyException> exceptions) throws IllegalManagedObjectNameException {
323        validateRelationDefinition(r);
324
325        // Empty names are not allowed.
326        if (name.trim().length() == 0) {
327            throw new IllegalManagedObjectNameException(name);
328        }
329
330        // If the relation uses a naming property definition then it must
331        // be a valid value.
332        PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
333        if (pd != null) {
334            try {
335                pd.decodeValue(name);
336            } catch (PropertyException e) {
337                throw new IllegalManagedObjectNameException(name, pd);
338            }
339        }
340
341        ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d, name);
342        return createNewManagedObject(d, childPath, pd, name, exceptions);
343    }
344
345    /** {@inheritDoc} */
346    @Override
347    public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild(
348        OptionalRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d,
349        Collection<PropertyException> exceptions) {
350        validateRelationDefinition(r);
351        ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d);
352        return createNewManagedObject(d, childPath, null, null, exceptions);
353    }
354
355    /** {@inheritDoc} */
356    @Override
357    public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild(
358        SetRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d,
359        Collection<PropertyException> exceptions) {
360        validateRelationDefinition(r);
361
362        ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d);
363        return createNewManagedObject(d, childPath, null, null, exceptions);
364    }
365
366    /** {@inheritDoc} */
367    @Override
368    public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild(
369        InstantiableRelationDefinition<C, S> r, String name) throws DefinitionDecodingException,
370        ManagedObjectDecodingException, ManagedObjectNotFoundException, ConcurrentModificationException,
371        LdapException {
372        validateRelationDefinition(r);
373        ensureThisManagedObjectExists();
374        Driver ctx = getDriver();
375        return ctx.getManagedObject(path.child(r, name));
376    }
377
378    /** {@inheritDoc} */
379    @Override
380    public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild(
381        OptionalRelationDefinition<C, S> r) throws DefinitionDecodingException, ManagedObjectDecodingException,
382        ManagedObjectNotFoundException, ConcurrentModificationException, LdapException {
383        validateRelationDefinition(r);
384        ensureThisManagedObjectExists();
385        Driver ctx = getDriver();
386        return ctx.getManagedObject(path.child(r));
387    }
388
389    /** {@inheritDoc} */
390    @Override
391    public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild(
392        SingletonRelationDefinition<C, S> r) throws DefinitionDecodingException, ManagedObjectDecodingException,
393        ManagedObjectNotFoundException, ConcurrentModificationException, LdapException {
394        validateRelationDefinition(r);
395        ensureThisManagedObjectExists();
396        Driver ctx = getDriver();
397        return ctx.getManagedObject(path.child(r));
398    }
399
400    /** {@inheritDoc} */
401    @Override
402    public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild(
403        SetRelationDefinition<C, S> r, String name) throws DefinitionDecodingException,
404        ManagedObjectDecodingException, ManagedObjectNotFoundException, ConcurrentModificationException,
405        LdapException {
406        validateRelationDefinition(r);
407        ensureThisManagedObjectExists();
408        Driver ctx = getDriver();
409
410        AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
411        AbstractManagedObjectDefinition<? extends C, ? extends S> cd;
412
413        try {
414            cd = d.getChild(name);
415        } catch (IllegalArgumentException e) {
416            // Unrecognized definition name - report this as a decoding
417            // exception.
418            throw new DefinitionDecodingException(d, Reason.WRONG_TYPE_INFORMATION);
419        }
420
421        return ctx.getManagedObject(path.child(r, cd));
422    }
423
424    /** {@inheritDoc} */
425    @Override
426    public final T getConfiguration() {
427        return definition.createClientConfiguration(this);
428    }
429
430    /** {@inheritDoc} */
431    @Override
432    public final ManagedObjectDefinition<T, ? extends Configuration> getManagedObjectDefinition() {
433        return definition;
434    }
435
436    /** {@inheritDoc} */
437    @Override
438    public final ManagedObjectPath<T, ? extends Configuration> getManagedObjectPath() {
439        return path;
440    }
441
442    /** {@inheritDoc} */
443    @Override
444    public final <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd) {
445        return new TreeSet<>(getProperty(pd).getDefaultValues());
446    }
447
448    /** {@inheritDoc} */
449    @Override
450    public final <P> P getPropertyValue(PropertyDefinition<P> pd) {
451        Set<P> values = getProperty(pd).getEffectiveValues();
452        if (!values.isEmpty()) {
453            return values.iterator().next();
454        }
455        return null;
456    }
457
458    /** {@inheritDoc} */
459    @Override
460    public final <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd) {
461        return new TreeSet<>(getProperty(pd).getEffectiveValues());
462    }
463
464    /** {@inheritDoc} */
465    @Override
466    public final <C extends ConfigurationClient, S extends Configuration> boolean hasChild(
467        OptionalRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException {
468        validateRelationDefinition(r);
469        Driver ctx = getDriver();
470        try {
471            return ctx.managedObjectExists(path.child(r));
472        } catch (ManagedObjectNotFoundException e) {
473            throw new ConcurrentModificationException();
474        }
475    }
476
477    /** {@inheritDoc} */
478    @Override
479    public final boolean isPropertyPresent(PropertyDefinition<?> pd) {
480        return !getProperty(pd).isEmpty();
481    }
482
483    /** {@inheritDoc} */
484    @Override
485    public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren(
486        InstantiableRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException {
487        return listChildren(r, r.getChildDefinition());
488    }
489
490    /** {@inheritDoc} */
491    @Override
492    public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren(
493        InstantiableRelationDefinition<C, S> r, AbstractManagedObjectDefinition<? extends C, ? extends S> d)
494            throws ConcurrentModificationException, LdapException {
495        validateRelationDefinition(r);
496        Driver ctx = getDriver();
497        try {
498            return ctx.listManagedObjects(path, r, d);
499        } catch (ManagedObjectNotFoundException e) {
500            throw new ConcurrentModificationException();
501        }
502    }
503
504    /** {@inheritDoc} */
505    @Override
506    public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren(
507        SetRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException {
508        return listChildren(r, r.getChildDefinition());
509    }
510
511    /** {@inheritDoc} */
512    @Override
513    public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren(
514        SetRelationDefinition<C, S> r, AbstractManagedObjectDefinition<? extends C, ? extends S> d)
515            throws ConcurrentModificationException, LdapException {
516        validateRelationDefinition(r);
517        Driver ctx = getDriver();
518        try {
519            return ctx.listManagedObjects(path, r, d);
520        } catch (ManagedObjectNotFoundException e) {
521            throw new ConcurrentModificationException();
522        }
523    }
524
525    /** {@inheritDoc} */
526    @Override
527    public final <C extends ConfigurationClient, S extends Configuration> void removeChild(
528        InstantiableRelationDefinition<C, S> r, String name) throws ManagedObjectNotFoundException,
529        OperationRejectedException, ConcurrentModificationException, LdapException {
530        validateRelationDefinition(r);
531        Driver ctx = getDriver();
532        boolean found;
533
534        try {
535            found = ctx.deleteManagedObject(path, r, name);
536        } catch (ManagedObjectNotFoundException e) {
537            throw new ConcurrentModificationException();
538        }
539
540        if (!found) {
541            throw new ManagedObjectNotFoundException();
542        }
543    }
544
545    /** {@inheritDoc} */
546    @Override
547    public final <C extends ConfigurationClient, S extends Configuration> void removeChild(
548        OptionalRelationDefinition<C, S> r) throws ManagedObjectNotFoundException, OperationRejectedException,
549        ConcurrentModificationException, LdapException {
550        validateRelationDefinition(r);
551        Driver ctx = getDriver();
552        boolean found;
553
554        try {
555            found = ctx.deleteManagedObject(path, r);
556        } catch (ManagedObjectNotFoundException e) {
557            throw new ConcurrentModificationException();
558        }
559
560        if (!found) {
561            throw new ManagedObjectNotFoundException();
562        }
563    }
564
565    /** {@inheritDoc} */
566    @Override
567    public final <C extends ConfigurationClient, S extends Configuration> void removeChild(
568        SetRelationDefinition<C, S> r, String name) throws ManagedObjectNotFoundException,
569        OperationRejectedException, ConcurrentModificationException, LdapException {
570        validateRelationDefinition(r);
571        Driver ctx = getDriver();
572        boolean found;
573
574        try {
575            found = ctx.deleteManagedObject(path, r, name);
576        } catch (ManagedObjectNotFoundException e) {
577            throw new ConcurrentModificationException();
578        }
579
580        if (!found) {
581            throw new ManagedObjectNotFoundException();
582        }
583    }
584
585    /** {@inheritDoc} */
586    @Override
587    public final <P> void setPropertyValue(PropertyDefinition<P> pd, P value) {
588        if (value != null) {
589            setPropertyValues(pd, Collections.singleton(value));
590        } else {
591            setPropertyValues(pd, Collections.<P> emptySet());
592        }
593    }
594
595    /** {@inheritDoc} */
596    @Override
597    public final <P> void setPropertyValues(PropertyDefinition<P> pd, Collection<P> values) {
598        if (pd.hasOption(PropertyOption.MONITORING)) {
599            throw PropertyException.propertyIsReadOnlyException(pd);
600        }
601
602        if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
603            throw PropertyException.propertyIsReadOnlyException(pd);
604        }
605
606        properties.setPropertyValues(pd, values);
607
608        // If this is a naming property then update the name.
609        if (pd.equals(namingPropertyDefinition)) {
610            // The property must be single-valued and mandatory.
611            String newName = pd.encodeValue(values.iterator().next());
612            path = path.rename(newName);
613        }
614    }
615
616    /** {@inheritDoc} */
617    @Override
618    public String toString() {
619        StringBuilder builder = new StringBuilder();
620
621        builder.append("{ TYPE=");
622        builder.append(definition.getName());
623        builder.append(", PATH=\"");
624        builder.append(path);
625        builder.append('\"');
626        for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
627            builder.append(", ");
628            builder.append(pd.getName());
629            builder.append('=');
630            builder.append(getPropertyValues(pd));
631        }
632        builder.append(" }");
633
634        return builder.toString();
635    }
636
637    /**
638     * Adds this new managed object.
639     *
640     * @throws ManagedObjectAlreadyExistsException
641     *             If the managed object cannot be added to the server because
642     *             it already exists.
643     * @throws ConcurrentModificationException
644     *             If the managed object's parent has been removed by another
645     *             client.
646     * @throws OperationRejectedException
647     *             If the managed object cannot be added due to some client-side
648     *             or server-side constraint which cannot be satisfied.
649     * @throws LdapException
650     *             If any other error occurs.
651     */
652    protected abstract void addNewManagedObject() throws LdapException, OperationRejectedException,
653        ConcurrentModificationException, ManagedObjectAlreadyExistsException;
654
655    /**
656     * Gets the management context driver associated with this managed object.
657     *
658     * @return Returns the management context driver associated with this
659     *         managed object.
660     */
661    protected abstract Driver getDriver();
662
663    /**
664     * Gets the naming property definition associated with this managed object.
665     *
666     * @return Returns the naming property definition associated with this
667     *         managed object, or <code>null</code> if this managed object does
668     *         not have a naming property.
669     */
670    protected final PropertyDefinition<?> getNamingPropertyDefinition() {
671        return namingPropertyDefinition;
672    }
673
674    /**
675     * Gets the property associated with the specified property definition.
676     *
677     * @param <P>
678     *            The underlying type of the property.
679     * @param pd
680     *            The Property definition.
681     * @return Returns the property associated with the specified property
682     *         definition.
683     * @throws IllegalArgumentException
684     *             If this property provider does not recognize the requested
685     *             property definition.
686     */
687    protected final <P> Property<P> getProperty(PropertyDefinition<P> pd) {
688        return properties.getProperty(pd);
689    }
690
691    /**
692     * Applies changes made to this managed object.
693     *
694     * @throws ConcurrentModificationException
695     *             If this managed object has been removed from the server by
696     *             another client.
697     * @throws OperationRejectedException
698     *             If the managed object cannot be added due to some client-side
699     *             or server-side constraint which cannot be satisfied.
700     * @throws LdapException
701     *             If any other error occurs.
702     */
703    protected abstract void modifyExistingManagedObject() throws ConcurrentModificationException,
704        OperationRejectedException, LdapException;
705
706    /**
707     * Creates a new managed object.
708     *
709     * @param <M>
710     *            The type of client configuration represented by the client
711     *            managed object.
712     * @param d
713     *            The managed object's definition.
714     * @param path
715     *            The managed object's path.
716     * @param properties
717     *            The managed object's properties.
718     * @param existsOnServer
719     *            Indicates whether or not the managed object exists on the
720     *            server (false means the managed object is new and has not been
721     *            committed).
722     * @param namingPropertyDefinition
723     *            Optional naming property definition.
724     * @return Returns the new managed object.
725     */
726    protected abstract <M extends ConfigurationClient> ManagedObject<M> newInstance(ManagedObjectDefinition<M, ?> d,
727        ManagedObjectPath<M, ?> path, PropertySet properties, boolean existsOnServer,
728        PropertyDefinition<?> namingPropertyDefinition);
729
730    /**
731     * Creates a new managed object with no active values, just default
732     * values.
733     */
734    private <M extends ConfigurationClient, P> ManagedObject<M> createNewManagedObject(
735        ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p, PropertyDefinition<P> namingPropertyDefinition,
736        String name, Collection<PropertyException> exceptions) {
737        PropertySet childProperties = new PropertySet();
738        for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
739            try {
740                createProperty(childProperties, p, pd);
741            } catch (PropertyException e) {
742                // Add the exception if requested.
743                if (exceptions != null) {
744                    exceptions.add(e);
745                }
746            }
747        }
748
749        // Set the naming property if there is one.
750        if (namingPropertyDefinition != null) {
751            P value = namingPropertyDefinition.decodeValue(name);
752            childProperties.setPropertyValues(namingPropertyDefinition, Collections.singleton(value));
753        }
754
755        return newInstance(d, p, childProperties, false, namingPropertyDefinition);
756    }
757
758    /** Create an empty property. */
759    private <P> void createProperty(PropertySet properties, ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd) {
760        try {
761            Driver context = getDriver();
762            Collection<P> defaultValues = context.findDefaultValues(p, pd, true);
763            properties.addProperty(pd, defaultValues, Collections.<P> emptySet());
764        } catch (PropertyException e) {
765            // Make sure that we have still created the property.
766            properties.addProperty(pd, Collections.<P> emptySet(), Collections.<P> emptySet());
767            throw e;
768        }
769    }
770
771    /** Makes sure that this managed object exists. */
772    private void ensureThisManagedObjectExists() throws ConcurrentModificationException, LdapException {
773        if (!path.isEmpty()) {
774            Driver ctx = getDriver();
775
776            try {
777                if (!ctx.managedObjectExists(path)) {
778                    throw new ConcurrentModificationException();
779                }
780            } catch (ManagedObjectNotFoundException e) {
781                throw new ConcurrentModificationException();
782            }
783        }
784    }
785
786    /**
787     * Validate that a relation definition belongs to this managed
788     * object.
789     */
790    private void validateRelationDefinition(RelationDefinition<?, ?> rd) {
791        ManagedObjectDefinition<T, ?> d = getManagedObjectDefinition();
792        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
793        if (tmp != rd) {
794            throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a "
795                + d.getName());
796        }
797    }
798
799}