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