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