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 2007-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.admin;
018
019import static org.opends.messages.AdminMessages.*;
020import static org.opends.server.util.StaticUtils.*;
021import static org.forgerock.util.Reject.*;
022
023import java.util.Collection;
024import java.util.Collections;
025import java.util.EnumSet;
026import java.util.HashMap;
027import java.util.Iterator;
028import java.util.LinkedList;
029import java.util.List;
030import java.util.Locale;
031import java.util.Map;
032import java.util.MissingResourceException;
033import java.util.SortedSet;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.opends.server.admin.client.AuthorizationException;
037import org.opends.server.admin.client.ClientConstraintHandler;
038import org.opends.server.admin.client.CommunicationException;
039import org.opends.server.admin.client.ManagedObject;
040import org.opends.server.admin.client.ManagedObjectDecodingException;
041import org.opends.server.admin.client.ManagementContext;
042import org.opends.server.admin.condition.Condition;
043import org.opends.server.admin.condition.Conditions;
044import org.opends.server.admin.server.ConfigurationDeleteListener;
045import org.opends.server.admin.server.ServerConstraintHandler;
046import org.opends.server.admin.server.ServerManagedObject;
047import org.opends.server.admin.server.ServerManagedObjectChangeListener;
048import org.opends.server.admin.server.ServerManagementContext;
049import org.opends.server.admin.std.meta.RootCfgDefn;
050import org.forgerock.opendj.config.server.ConfigException;
051import org.forgerock.i18n.slf4j.LocalizedLogger;
052import org.forgerock.opendj.config.server.ConfigChangeResult;
053import org.forgerock.opendj.ldap.DN;
054
055/**
056 * Aggregation property definition.
057 * <p>
058 * An aggregation property names one or more managed objects which are
059 * required by the managed object associated with this property. An
060 * aggregation property definition takes care to perform referential
061 * integrity checks: referenced managed objects cannot be deleted. Nor
062 * can an aggregation reference non-existent managed objects.
063 * Referential integrity checks are <b>not</b> performed during value
064 * validation. Instead they are performed when changes to the managed
065 * object are committed.
066 * <p>
067 * An aggregation property definition can optionally identify two
068 * properties:
069 * <ul>
070 * <li>an <code>enabled</code> property in the aggregated managed
071 * object - the property must be a {@link BooleanPropertyDefinition}
072 * and indicate whether the aggregated managed object is enabled or
073 * not. If specified, the administration framework will prevent the
074 * aggregated managed object from being disabled while it is
075 * referenced
076 * <li>an <code>enabled</code> property in this property's managed
077 * object - the property must be a {@link BooleanPropertyDefinition}
078 * and indicate whether this property's managed object is enabled or
079 * not. If specified, and as long as there is an equivalent
080 * <code>enabled</code> property defined for the aggregated managed
081 * object, the <code>enabled</code> property in the aggregated
082 * managed object will only be checked when this property is true.
083 * </ul>
084 * In other words, these properties can be used to make sure that
085 * referenced managed objects are not disabled while they are
086 * referenced.
087 *
088 * @param <C>
089 *          The type of client managed object configuration that this
090 *          aggregation property definition refers to.
091 * @param <S>
092 *          The type of server managed object configuration that this
093 *          aggregation property definition refers to.
094 */
095public final class AggregationPropertyDefinition
096    <C extends ConfigurationClient, S extends Configuration>
097    extends PropertyDefinition<String> {
098
099  /**
100   * An interface for incrementally constructing aggregation property
101   * definitions.
102   *
103   * @param <C>
104   *          The type of client managed object configuration that
105   *          this aggregation property definition refers to.
106   * @param <S>
107   *          The type of server managed object configuration that
108   *          this aggregation property definition refers to.
109   */
110  public static class Builder
111      <C extends ConfigurationClient, S extends Configuration>
112      extends AbstractBuilder<String, AggregationPropertyDefinition<C, S>> {
113
114    /**
115     * The string representation of the managed object path specifying
116     * the parent of the aggregated managed objects.
117     */
118    private String parentPathString;
119
120    /**
121     * The name of a relation in the parent managed object which
122     * contains the aggregated managed objects.
123     */
124    private String rdName;
125
126    /**
127     * The condition which is used to determine if a referenced
128     * managed object is enabled.
129     */
130    private Condition targetIsEnabledCondition = Conditions.TRUE;
131
132    /**
133     * The condition which is used to determine whether or not
134     * referenced managed objects need to be enabled.
135     */
136    private Condition targetNeedsEnablingCondition = Conditions.TRUE;
137
138
139
140    /** Private constructor. */
141    private Builder(AbstractManagedObjectDefinition<?, ?> d,
142        String propertyName) {
143      super(d, propertyName);
144    }
145
146
147
148    /**
149     * Sets the name of the managed object which is the parent of the
150     * aggregated managed objects.
151     * <p>
152     * This must be defined before the property definition can be
153     * built.
154     *
155     * @param pathString
156     *          The string representation of the managed object path
157     *          specifying the parent of the aggregated managed
158     *          objects.
159     */
160    public final void setParentPath(String pathString) {
161      this.parentPathString = pathString;
162    }
163
164
165
166    /**
167     * Sets the relation in the parent managed object which contains
168     * the aggregated managed objects.
169     * <p>
170     * This must be defined before the property definition can be
171     * built.
172     *
173     * @param rdName
174     *          The name of a relation in the parent managed object
175     *          which contains the aggregated managed objects.
176     */
177    public final void setRelationDefinition(String rdName) {
178      this.rdName = rdName;
179    }
180
181
182
183    /**
184     * Sets the condition which is used to determine if a referenced
185     * managed object is enabled. By default referenced managed
186     * objects are assumed to always be enabled.
187     *
188     * @param condition
189     *          The condition which is used to determine if a
190     *          referenced managed object is enabled.
191     */
192    public final void setTargetIsEnabledCondition(Condition condition) {
193      this.targetIsEnabledCondition = condition;
194    }
195
196
197
198    /**
199     * Sets the condition which is used to determine whether or not
200     * referenced managed objects need to be enabled. By default
201     * referenced managed objects must always be enabled.
202     *
203     * @param condition
204     *          The condition which is used to determine whether or
205     *          not referenced managed objects need to be enabled.
206     */
207    public final void setTargetNeedsEnablingCondition(Condition condition) {
208      this.targetNeedsEnablingCondition = condition;
209    }
210
211
212
213    /** {@inheritDoc} */
214    @Override
215    protected AggregationPropertyDefinition<C, S> buildInstance(
216        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
217        EnumSet<PropertyOption> options, AdministratorAction adminAction,
218        DefaultBehaviorProvider<String> defaultBehavior) {
219      // Make sure that the parent path has been defined.
220      if (parentPathString == null) {
221        throw new IllegalStateException("Parent path undefined");
222      }
223
224      // Make sure that the relation definition has been defined.
225      if (rdName == null) {
226        throw new IllegalStateException("Relation definition undefined");
227      }
228
229      return new AggregationPropertyDefinition<>(d, propertyName, options,
230          adminAction, defaultBehavior, parentPathString, rdName,
231          targetNeedsEnablingCondition, targetIsEnabledCondition);
232    }
233  }
234
235
236
237  /**
238   * A change listener which prevents the named component from being
239   * disabled.
240   */
241  private class ReferentialIntegrityChangeListener implements
242      ServerManagedObjectChangeListener<S> {
243
244    /**
245     * The error message which should be returned if an attempt is
246     * made to disable the referenced component.
247     */
248    private final LocalizableMessage message;
249
250    /** The path of the referenced component. */
251    private final ManagedObjectPath<C, S> path;
252
253
254
255    /** Creates a new referential integrity delete listener. */
256    private ReferentialIntegrityChangeListener(ManagedObjectPath<C, S> path,
257        LocalizableMessage message) {
258      this.path = path;
259      this.message = message;
260    }
261
262
263
264    /** {@inheritDoc} */
265    public ConfigChangeResult applyConfigurationChange(
266        ServerManagedObject<? extends S> mo) {
267      try {
268        if (targetIsEnabledCondition.evaluate(mo)) {
269          return new ConfigChangeResult();
270        }
271      } catch (ConfigException e) {
272        // This should not happen - ignore it and throw an exception
273        // anyway below.
274      }
275
276      // This should not happen - the previous call-back should have
277      // trapped this.
278      throw new IllegalStateException("Attempting to disable a referenced "
279          + relationDefinition.getChildDefinition().getUserFriendlyName());
280    }
281
282
283
284    /** {@inheritDoc} */
285    public boolean isConfigurationChangeAcceptable(
286        ServerManagedObject<? extends S> mo,
287        List<LocalizableMessage> unacceptableReasons) {
288      // Always prevent the referenced component from being
289      // disabled.
290      try {
291        if (!targetIsEnabledCondition.evaluate(mo)) {
292          unacceptableReasons.add(message);
293          return false;
294        } else {
295          return true;
296        }
297      } catch (ConfigException e) {
298        // The condition could not be evaluated.
299        logger.traceException(e);
300        logger.error(ERR_REFINT_UNABLE_TO_EVALUATE_TARGET_CONDITION,
301            mo.getManagedObjectDefinition().getUserFriendlyName(), mo.getDN(), getExceptionMessage(e));
302        unacceptableReasons.add(message);
303        return false;
304      }
305    }
306
307
308
309    /** Gets the path associated with this listener. */
310    private ManagedObjectPath<C, S> getManagedObjectPath() {
311      return path;
312    }
313
314  }
315
316
317
318  /**
319   * A delete listener which prevents the named component from being
320   * deleted.
321   */
322  private class ReferentialIntegrityDeleteListener implements
323      ConfigurationDeleteListener<S> {
324
325    /** The DN of the referenced configuration entry. */
326    private final DN dn;
327
328    /**
329     * The error message which should be returned if an attempt is
330     * made to delete the referenced component.
331     */
332    private final LocalizableMessage message;
333
334
335
336    /** Creates a new referential integrity delete listener. */
337    private ReferentialIntegrityDeleteListener(DN dn, LocalizableMessage message) {
338      this.dn = dn;
339      this.message = message;
340    }
341
342
343
344    /** {@inheritDoc} */
345    public ConfigChangeResult applyConfigurationDelete(S configuration) {
346      // This should not happen - the
347      // isConfigurationDeleteAcceptable() call-back should have
348      // trapped this.
349      if (configuration.dn().equals(dn)) {
350        // This should not happen - the
351        // isConfigurationDeleteAcceptable() call-back should have
352        // trapped this.
353        throw new IllegalStateException("Attempting to delete a referenced "
354            + relationDefinition.getChildDefinition().getUserFriendlyName());
355      } else {
356        return new ConfigChangeResult();
357      }
358    }
359
360
361
362    /** {@inheritDoc} */
363    public boolean isConfigurationDeleteAcceptable(S configuration,
364        List<LocalizableMessage> unacceptableReasons) {
365      if (configuration.dn().equals(dn)) {
366        // Always prevent deletion of the referenced component.
367        unacceptableReasons.add(message);
368        return false;
369      }
370
371      return true;
372    }
373
374  }
375
376
377
378  /**
379   * The server-side constraint handler implementation.
380   */
381  private class ServerHandler extends ServerConstraintHandler {
382
383    /** {@inheritDoc} */
384    @Override
385    public boolean isUsable(ServerManagedObject<?> managedObject,
386        Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
387      SortedSet<String> names = managedObject
388          .getPropertyValues(AggregationPropertyDefinition.this);
389      ServerManagementContext context = ServerManagementContext.getInstance();
390      LocalizableMessage thisUFN = managedObject.getManagedObjectDefinition()
391          .getUserFriendlyName();
392      String thisDN = managedObject.getDN().toString();
393      LocalizableMessage thatUFN = getRelationDefinition().getUserFriendlyName();
394
395      boolean isUsable = true;
396      boolean needsEnabling = targetNeedsEnablingCondition
397          .evaluate(managedObject);
398      for (String name : names) {
399        ManagedObjectPath<C, S> path = getChildPath(name);
400        String thatDN = path.toDN().toString();
401
402        if (!context.managedObjectExists(path)) {
403          LocalizableMessage msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name,
404              getName(), thisUFN, thisDN, thatUFN, thatDN);
405          unacceptableReasons.add(msg);
406          isUsable = false;
407        } else if (needsEnabling) {
408          // Check that the referenced component is enabled if
409          // required.
410          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
411          if (!targetIsEnabledCondition.evaluate(ref)) {
412            LocalizableMessage msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name,
413                getName(), thisUFN, thisDN, thatUFN, thatDN);
414            unacceptableReasons.add(msg);
415            isUsable = false;
416          }
417        }
418      }
419
420      return isUsable;
421    }
422
423
424
425    /** {@inheritDoc} */
426    @Override
427    public void performPostAdd(ServerManagedObject<?> managedObject)
428        throws ConfigException {
429      // First make sure existing listeners associated with this
430      // managed object are removed. This is required in order to
431      // prevent multiple change listener registrations from
432      // occurring, for example if this call-back is invoked multiple
433      // times after the same add event.
434      performPostDelete(managedObject);
435
436      // Add change and delete listeners against all referenced
437      // components.
438      LocalizableMessage thisUFN = managedObject.getManagedObjectDefinition()
439          .getUserFriendlyName();
440      String thisDN = managedObject.getDN().toString();
441      LocalizableMessage thatUFN = getRelationDefinition().getUserFriendlyName();
442
443      // Referenced managed objects will only need a change listener
444      // if they have can be disabled.
445      boolean needsChangeListeners = targetNeedsEnablingCondition
446          .evaluate(managedObject);
447
448      // Delete listeners need to be registered against the parent
449      // entry of the referenced components.
450      ServerManagementContext context = ServerManagementContext.getInstance();
451      ManagedObjectPath<?, ?> parentPath = getParentPath();
452      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
453
454      // Create entries in the listener tables.
455      List<ReferentialIntegrityDeleteListener> dlist = new LinkedList<>();
456      deleteListeners.put(managedObject.getDN(), dlist);
457
458      List<ReferentialIntegrityChangeListener> clist = new LinkedList<>();
459      changeListeners.put(managedObject.getDN(), clist);
460
461      for (String name : managedObject
462          .getPropertyValues(AggregationPropertyDefinition.this)) {
463        ManagedObjectPath<C, S> path = getChildPath(name);
464        DN dn = path.toDN();
465        String thatDN = dn.toString();
466
467        // Register the delete listener.
468        LocalizableMessage msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN,
469            getName(), thisUFN, thisDN);
470        ReferentialIntegrityDeleteListener dl =
471          new ReferentialIntegrityDeleteListener(dn, msg);
472        parent.registerDeleteListener(getRelationDefinition(), dl);
473        dlist.add(dl);
474
475        // Register the change listener if required.
476        if (needsChangeListeners) {
477          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
478          msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN,
479              getName(), thisUFN, thisDN);
480          ReferentialIntegrityChangeListener cl =
481            new ReferentialIntegrityChangeListener(path, msg);
482          ref.registerChangeListener(cl);
483          clist.add(cl);
484        }
485      }
486    }
487
488
489
490    /** {@inheritDoc} */
491    @Override
492    public void performPostDelete(ServerManagedObject<?> managedObject)
493        throws ConfigException {
494      // Remove any registered delete and change listeners.
495      ServerManagementContext context = ServerManagementContext.getInstance();
496      DN dn = managedObject.getDN();
497
498      // Delete listeners need to be deregistered against the parent
499      // entry of the referenced components.
500      ManagedObjectPath<?, ?> parentPath = getParentPath();
501      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
502      if (deleteListeners.containsKey(dn)) {
503        for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
504          parent.deregisterDeleteListener(getRelationDefinition(), dl);
505        }
506        deleteListeners.remove(dn);
507      }
508
509      // Change listeners need to be deregistered from their
510      // associated referenced component.
511      if (changeListeners.containsKey(dn)) {
512        for (ReferentialIntegrityChangeListener cl : changeListeners.get(dn)) {
513          ManagedObjectPath<C, S> path = cl.getManagedObjectPath();
514          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
515          ref.deregisterChangeListener(cl);
516        }
517        changeListeners.remove(dn);
518      }
519    }
520
521
522
523    /** {@inheritDoc} */
524    @Override
525    public void performPostModify(ServerManagedObject<?> managedObject)
526        throws ConfigException {
527      // Remove all the constraints associated with this managed
528      // object and then re-register them.
529      performPostDelete(managedObject);
530      performPostAdd(managedObject);
531    }
532  }
533
534
535
536  /**
537   * The client-side constraint handler implementation which enforces
538   * referential integrity when aggregating managed objects are added
539   * or modified.
540   */
541  private class SourceClientHandler extends ClientConstraintHandler {
542
543    /** {@inheritDoc} */
544    @Override
545    public boolean isAddAcceptable(ManagementContext context,
546        ManagedObject<?> managedObject, Collection<LocalizableMessage> unacceptableReasons)
547        throws AuthorizationException, CommunicationException {
548      // If all of this managed object's "enabled" properties are true
549      // then any referenced managed objects must also be enabled.
550      boolean needsEnabling = targetNeedsEnablingCondition.evaluate(context,
551          managedObject);
552
553      // Check the referenced managed objects exist and, if required,
554      // are enabled.
555      boolean isAcceptable = true;
556      LocalizableMessage ufn = getRelationDefinition().getUserFriendlyName();
557      for (String name : managedObject
558          .getPropertyValues(AggregationPropertyDefinition.this)) {
559        // Retrieve the referenced managed object and make sure it
560        // exists.
561        ManagedObjectPath<?, ?> path = getChildPath(name);
562        ManagedObject<?> ref;
563        try {
564          ref = context.getManagedObject(path);
565        } catch (DefinitionDecodingException | ManagedObjectDecodingException e) {
566          LocalizableMessage msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name,
567              getName(), e.getMessageObject());
568          unacceptableReasons.add(msg);
569          isAcceptable = false;
570          continue;
571        } catch (ManagedObjectNotFoundException e) {
572          LocalizableMessage msg = ERR_CLIENT_REFINT_TARGET_DANGLING_REFERENCE.get(ufn,
573              name, getName());
574          unacceptableReasons.add(msg);
575          isAcceptable = false;
576          continue;
577        }
578
579        // Make sure the reference managed object is enabled.
580        if (needsEnabling
581            && !targetIsEnabledCondition.evaluate(context, ref)) {
582          unacceptableReasons.add(ERR_CLIENT_REFINT_TARGET_DISABLED.get(ufn, name, getName()));
583          isAcceptable = false;
584        }
585      }
586      return isAcceptable;
587    }
588
589
590
591    /** {@inheritDoc} */
592    @Override
593    public boolean isModifyAcceptable(ManagementContext context,
594        ManagedObject<?> managedObject, Collection<LocalizableMessage> unacceptableReasons)
595        throws AuthorizationException, CommunicationException {
596      // The same constraint applies as for adds.
597      return isAddAcceptable(context, managedObject, unacceptableReasons);
598    }
599
600  }
601
602
603
604  /**
605   * The client-side constraint handler implementation which enforces
606   * referential integrity when aggregated managed objects are deleted
607   * or modified.
608   */
609  private class TargetClientHandler extends ClientConstraintHandler {
610
611    /** {@inheritDoc} */
612    @Override
613    public boolean isDeleteAcceptable(ManagementContext context,
614        ManagedObjectPath<?, ?> path, Collection<LocalizableMessage> unacceptableReasons)
615        throws AuthorizationException, CommunicationException {
616      // Any references to the deleted managed object should cause a
617      // constraint violation.
618      boolean isAcceptable = true;
619      for (ManagedObject<?> mo : findReferences(context,
620          getManagedObjectDefinition(), path.getName())) {
621        LocalizableMessage msg;
622        String name = mo.getManagedObjectPath().getName();
623        if (name == null) {
624          msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITHOUT_NAME.get(
625              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
626              getManagedObjectDefinition().getUserFriendlyName());
627        } else {
628          msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITH_NAME.get(
629              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
630              name, getManagedObjectDefinition().getUserFriendlyName());
631        }
632        unacceptableReasons.add(msg);
633        isAcceptable = false;
634      }
635      return isAcceptable;
636    }
637
638
639
640    /** {@inheritDoc} */
641    @Override
642    public boolean isModifyAcceptable(ManagementContext context,
643        ManagedObject<?> managedObject, Collection<LocalizableMessage> unacceptableReasons)
644        throws AuthorizationException, CommunicationException {
645      // If the modified managed object is disabled and there are some
646      // active references then refuse the change.
647      if (targetIsEnabledCondition.evaluate(context, managedObject)) {
648        return true;
649      }
650
651      // The referenced managed object is disabled. Need to check for
652      // active references.
653      boolean isAcceptable = true;
654      for (ManagedObject<?> mo : findReferences(context,
655          getManagedObjectDefinition(), managedObject.getManagedObjectPath()
656              .getName())) {
657        if (targetNeedsEnablingCondition.evaluate(context, mo)) {
658          LocalizableMessage msg;
659          String name = mo.getManagedObjectPath().getName();
660          if (name == null) {
661            msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITHOUT_NAME.get(
662                managedObject.getManagedObjectDefinition().getUserFriendlyName(),
663                getName(), mo.getManagedObjectDefinition().getUserFriendlyName());
664          } else {
665            msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITH_NAME.get(
666                managedObject.getManagedObjectDefinition().getUserFriendlyName(),
667                getName(), mo.getManagedObjectDefinition().getUserFriendlyName(), name);
668          }
669          unacceptableReasons.add(msg);
670          isAcceptable = false;
671        }
672      }
673      return isAcceptable;
674    }
675
676
677
678    /**
679     * Find all managed objects which reference the named managed
680     * object using this property.
681     */
682    private <CC extends ConfigurationClient>
683        List<ManagedObject<? extends CC>> findReferences(
684        ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod,
685        String name) throws AuthorizationException, CommunicationException {
686      List<ManagedObject<? extends CC>> instances = findInstances(context, mod);
687
688      Iterator<ManagedObject<? extends CC>> i = instances.iterator();
689      while (i.hasNext()) {
690        ManagedObject<? extends CC> mo = i.next();
691        boolean hasReference = false;
692
693        for (String value : mo
694            .getPropertyValues(AggregationPropertyDefinition.this)) {
695          if (compare(value, name) == 0) {
696            hasReference = true;
697            break;
698          }
699        }
700
701        if (!hasReference) {
702          i.remove();
703        }
704      }
705
706      return instances;
707    }
708
709
710
711    /** Find all instances of a specific type of managed object. */
712    @SuppressWarnings("unchecked")
713    private <CC extends ConfigurationClient>
714        List<ManagedObject<? extends CC>> findInstances(
715        ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod)
716        throws AuthorizationException, CommunicationException {
717      List<ManagedObject<? extends CC>> instances = new LinkedList<>();
718
719      if (mod == RootCfgDefn.getInstance()) {
720        instances.add((ManagedObject<? extends CC>) context
721            .getRootConfigurationManagedObject());
722      } else {
723        for (RelationDefinition<? super CC, ?> rd : mod
724            .getAllReverseRelationDefinitions()) {
725          for (ManagedObject<?> parent : findInstances(context, rd
726              .getParentDefinition())) {
727            try {
728              if (rd instanceof SingletonRelationDefinition) {
729                SingletonRelationDefinition<? super CC, ?> srd =
730                  (SingletonRelationDefinition<? super CC, ?>) rd;
731                ManagedObject<?> mo = parent.getChild(srd);
732                if (mo.getManagedObjectDefinition().isChildOf(mod)) {
733                  instances.add((ManagedObject<? extends CC>) mo);
734                }
735              } else if (rd instanceof OptionalRelationDefinition) {
736                OptionalRelationDefinition<? super CC, ?> ord =
737                  (OptionalRelationDefinition<? super CC, ?>) rd;
738                ManagedObject<?> mo = parent.getChild(ord);
739                if (mo.getManagedObjectDefinition().isChildOf(mod)) {
740                  instances.add((ManagedObject<? extends CC>) mo);
741                }
742              } else if (rd instanceof InstantiableRelationDefinition) {
743                InstantiableRelationDefinition<? super CC, ?> ird =
744                  (InstantiableRelationDefinition<? super CC, ?>) rd;
745
746                for (String name : parent.listChildren(ird)) {
747                  ManagedObject<?> mo = parent.getChild(ird, name);
748                  if (mo.getManagedObjectDefinition().isChildOf(mod)) {
749                    instances.add((ManagedObject<? extends CC>) mo);
750                  }
751                }
752              }
753            } catch (OperationsException e) {
754              // Ignore all operations exceptions.
755            }
756          }
757        }
758      }
759
760      return instances;
761    }
762  }
763  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
764
765
766
767  /**
768   * Creates an aggregation property definition builder.
769   *
770   * @param <C>
771   *          The type of client managed object configuration that
772   *          this aggregation property definition refers to.
773   * @param <S>
774   *          The type of server managed object configuration that
775   *          this aggregation property definition refers to.
776   * @param d
777   *          The managed object definition associated with this
778   *          property definition.
779   * @param propertyName
780   *          The property name.
781   * @return Returns the new aggregation property definition builder.
782   */
783  public static <C extends ConfigurationClient, S extends Configuration>
784      Builder<C, S> createBuilder(
785      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
786    return new Builder<>(d, propertyName);
787  }
788
789  /**
790   * The active server-side referential integrity change listeners
791   * associated with this property.
792   */
793  private final Map<DN, List<ReferentialIntegrityChangeListener>> changeListeners = new HashMap<>();
794
795  /**
796   * The active server-side referential integrity delete listeners
797   * associated with this property.
798   */
799  private final Map<DN, List<ReferentialIntegrityDeleteListener>> deleteListeners = new HashMap<>();
800
801  /**
802   * The name of the managed object which is the parent of the
803   * aggregated managed objects.
804   */
805  private ManagedObjectPath<?, ?> parentPath;
806
807  /**
808   * The string representation of the managed object path specifying
809   * the parent of the aggregated managed objects.
810   */
811  private final String parentPathString;
812
813  /**
814   * The name of a relation in the parent managed object which
815   * contains the aggregated managed objects.
816   */
817  private final String rdName;
818
819  /**
820   * The relation in the parent managed object which contains the
821   * aggregated managed objects.
822   */
823  private InstantiableRelationDefinition<C, S> relationDefinition;
824
825  /** The source constraint. */
826  private final Constraint sourceConstraint;
827
828  /**
829   * The condition which is used to determine if a referenced managed
830   * object is enabled.
831   */
832  private final Condition targetIsEnabledCondition;
833
834  /**
835   * The condition which is used to determine whether or not
836   * referenced managed objects need to be enabled.
837   */
838  private final Condition targetNeedsEnablingCondition;
839
840
841
842  /** Private constructor. */
843  private AggregationPropertyDefinition(
844      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
845      EnumSet<PropertyOption> options, AdministratorAction adminAction,
846      DefaultBehaviorProvider<String> defaultBehavior, String parentPathString,
847      String rdName, Condition targetNeedsEnablingCondition,
848      Condition targetIsEnabledCondition) {
849    super(d, String.class, propertyName, options, adminAction, defaultBehavior);
850
851    this.parentPathString = parentPathString;
852    this.rdName = rdName;
853    this.targetNeedsEnablingCondition = targetNeedsEnablingCondition;
854    this.targetIsEnabledCondition = targetIsEnabledCondition;
855    this.sourceConstraint = new Constraint() {
856
857      /** {@inheritDoc} */
858      public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
859        ClientConstraintHandler handler = new SourceClientHandler();
860        return Collections.singleton(handler);
861      }
862
863
864
865      /** {@inheritDoc} */
866      public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
867        ServerConstraintHandler handler = new ServerHandler();
868        return Collections.singleton(handler);
869      }
870    };
871  }
872
873
874
875  /** {@inheritDoc} */
876  @Override
877  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
878    return v.visitAggregation(this, p);
879  }
880
881
882
883  /** {@inheritDoc} */
884  @Override
885  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
886    return v.visitAggregation(this, value, p);
887  }
888
889
890
891  /** {@inheritDoc} */
892  @Override
893  public String decodeValue(String value)
894      throws PropertyException {
895    ifNull(value);
896
897    try {
898      validateValue(value);
899      return value;
900    } catch (PropertyException e) {
901      throw PropertyException.illegalPropertyValueException(this, value);
902    }
903  }
904
905
906
907  /**
908   * Constructs a DN for a referenced managed object having the
909   * provided name. This method is implemented by first calling
910   * {@link #getChildPath(String)} and then invoking
911   * {@code ManagedObjectPath.toDN()} on the returned path.
912   *
913   * @param name
914   *          The name of the child managed object.
915   * @return Returns a DN for a referenced managed object having the
916   *         provided name.
917   */
918  public final DN getChildDN(String name) {
919    return getChildPath(name).toDN();
920  }
921
922
923
924  /**
925   * Constructs a managed object path for a referenced managed object
926   * having the provided name.
927   *
928   * @param name
929   *          The name of the child managed object.
930   * @return Returns a managed object path for a referenced managed
931   *         object having the provided name.
932   */
933  public final ManagedObjectPath<C, S> getChildPath(String name) {
934    return parentPath.child(relationDefinition, name);
935  }
936
937
938
939  /**
940   * Gets the name of the managed object which is the parent of the
941   * aggregated managed objects.
942   *
943   * @return Returns the name of the managed object which is the
944   *         parent of the aggregated managed objects.
945   */
946  public final ManagedObjectPath<?, ?> getParentPath() {
947    return parentPath;
948  }
949
950
951
952  /**
953   * Gets the relation in the parent managed object which contains the
954   * aggregated managed objects.
955   *
956   * @return Returns the relation in the parent managed object which
957   *         contains the aggregated managed objects.
958   */
959  public final InstantiableRelationDefinition<C, S> getRelationDefinition() {
960    return relationDefinition;
961  }
962
963
964
965  /**
966   * Gets the constraint which should be enforced on the aggregating
967   * managed object.
968   *
969   * @return Returns the constraint which should be enforced on the
970   *         aggregating managed object.
971   */
972  public final Constraint getSourceConstraint() {
973    return sourceConstraint;
974  }
975
976
977
978  /**
979   * Gets the optional constraint synopsis of this aggregation
980   * property definition in the default locale. The constraint
981   * synopsis describes when and how referenced managed objects must
982   * be enabled. When there are no constraints between the source
983   * managed object and the objects it references through this
984   * aggregation, <code>null</code> is returned.
985   *
986   * @return Returns the optional constraint synopsis of this
987   *         aggregation property definition in the default locale, or
988   *         <code>null</code> if there is no constraint synopsis.
989   */
990  public final LocalizableMessage getSourceConstraintSynopsis() {
991    return getSourceConstraintSynopsis(Locale.getDefault());
992  }
993
994
995
996  /**
997   * Gets the optional constraint synopsis of this aggregation
998   * property definition in the specified locale.The constraint
999   * synopsis describes when and how referenced managed objects must
1000   * be enabled. When there are no constraints between the source
1001   * managed object and the objects it references through this
1002   * aggregation, <code>null</code> is returned.
1003   *
1004   * @param locale
1005   *          The locale.
1006   * @return Returns the optional constraint synopsis of this
1007   *         aggregation property definition in the specified locale,
1008   *         or <code>null</code> if there is no constraint
1009   *         synopsis.
1010   */
1011  public final LocalizableMessage getSourceConstraintSynopsis(Locale locale) {
1012    ManagedObjectDefinitionI18NResource resource =
1013      ManagedObjectDefinitionI18NResource.getInstance();
1014    String property = "property." + getName()
1015        + ".syntax.aggregation.constraint-synopsis";
1016    try {
1017      return resource
1018          .getMessage(getManagedObjectDefinition(), property, locale);
1019    } catch (MissingResourceException e) {
1020      return null;
1021    }
1022  }
1023
1024
1025
1026  /**
1027   * Gets the condition which is used to determine if a referenced
1028   * managed object is enabled.
1029   *
1030   * @return Returns the condition which is used to determine if a
1031   *         referenced managed object is enabled.
1032   */
1033  public final Condition getTargetIsEnabledCondition() {
1034    return targetIsEnabledCondition;
1035  }
1036
1037
1038
1039  /**
1040   * Gets the condition which is used to determine whether or not
1041   * referenced managed objects need to be enabled.
1042   *
1043   * @return Returns the condition which is used to determine whether
1044   *         or not referenced managed objects need to be enabled.
1045   */
1046  public final Condition getTargetNeedsEnablingCondition() {
1047    return targetNeedsEnablingCondition;
1048  }
1049
1050
1051
1052  /** {@inheritDoc} */
1053  @Override
1054  public String normalizeValue(String value)
1055      throws PropertyException {
1056    try {
1057      Reference<C, S> reference = Reference.parseName(parentPath,
1058          relationDefinition, value);
1059      return reference.getNormalizedName();
1060    } catch (IllegalArgumentException e) {
1061      throw PropertyException.illegalPropertyValueException(this, value);
1062    }
1063  }
1064
1065
1066
1067  /** {@inheritDoc} */
1068  @Override
1069  public void toString(StringBuilder builder) {
1070    super.toString(builder);
1071
1072    builder.append(" parentPath=").append(parentPath);
1073    builder.append(" relationDefinition=").append(relationDefinition.getName());
1074    builder.append(" targetNeedsEnablingCondition=").append(targetNeedsEnablingCondition);
1075    builder.append(" targetIsEnabledCondition=").append(targetIsEnabledCondition);
1076  }
1077
1078
1079
1080  /** {@inheritDoc} */
1081  @Override
1082  public void validateValue(String value) throws PropertyException {
1083    try {
1084      Reference.parseName(parentPath, relationDefinition, value);
1085    } catch (IllegalArgumentException e) {
1086      throw PropertyException.illegalPropertyValueException(this, value);
1087    }
1088  }
1089
1090
1091
1092  /** {@inheritDoc} */
1093  @SuppressWarnings("unchecked")
1094  @Override
1095  public void initialize() throws Exception {
1096    // Decode the path.
1097    parentPath = ManagedObjectPath.valueOf(parentPathString);
1098
1099    // Decode the relation definition.
1100    AbstractManagedObjectDefinition<?, ?> parent = parentPath
1101        .getManagedObjectDefinition();
1102    RelationDefinition<?, ?> rd = parent.getRelationDefinition(rdName);
1103    relationDefinition = (InstantiableRelationDefinition<C, S>) rd;
1104
1105    // Now decode the conditions.
1106    targetNeedsEnablingCondition.initialize(getManagedObjectDefinition());
1107    targetIsEnabledCondition.initialize(rd.getChildDefinition());
1108
1109    // Register a client-side constraint with the referenced
1110    // definition. This will be used to enforce referential integrity
1111    // for actions performed against referenced managed objects.
1112    Constraint constraint = new Constraint() {
1113
1114      /** {@inheritDoc} */
1115      public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
1116        ClientConstraintHandler handler = new TargetClientHandler();
1117        return Collections.singleton(handler);
1118      }
1119
1120
1121
1122      /** {@inheritDoc} */
1123      public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
1124        return Collections.emptyList();
1125      }
1126    };
1127
1128    rd.getChildDefinition().registerConstraint(constraint);
1129  }
1130
1131}