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