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