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