mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
24.38.2007 f2886f88f7ebebea3a925d0e69bbb8f971ffbb3d
Fix issue 1449: aggregation support.

This change implements the remaining required functionality for issue 1449. It adds client side support for enforcing referential integrity between components.
3 files modified
525 ■■■■ changed files
opends/src/messages/messages/admin.properties 15 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java 471 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/AggregationTest.java 39 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/admin.properties
@@ -294,3 +294,18 @@
FATAL_ERR_CLASS_LOADER_CANNOT_LOAD_CORE_126=The core administration \
 classes could not be loaded from manifest file %s because an unexpected \
 error occurred: %s
SEVERE_ERR_CLIENT_REFINT_TARGET_DANGLING_REFERENCE_127=The %s "%s" referenced in \
 property "%s" does not exist
SEVERE_ERR_CLIENT_REFINT_TARGET_INVALID_128=The %s "%s" referenced in \
 property "%s" exists but has an invalid configuration: %s
SEVERE_ERR_CLIENT_REFINT_TARGET_DISABLED_129=The %s "%s" referenced in \
 property "%s" is disabled
SEVERE_ERR_CLIENT_REFINT_CANNOT_DELETE_WITH_NAME_130=The "%s" property \
 in the %s called "%s" references this %s
SEVERE_ERR_CLIENT_REFINT_CANNOT_DELETE_WITHOUT_NAME_131=The "%s" property \
 in the %s references this %s
SEVERE_ERR_CLIENT_REFINT_CANNOT_DISABLE_WITH_NAME_132=This %s cannot be \
 disabled because it is referenced by the "%s" property in the %s called "%s"
SEVERE_ERR_CLIENT_REFINT_CANNOT_DISABLE_WITHOUT_NAME_133=This %s cannot be \
 disabled because it is referenced by the "%s" property in the %s
opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -41,7 +41,12 @@
import java.util.SortedSet;
import org.opends.messages.Message;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.ClientConstraintHandler;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerConstraintHandler;
@@ -143,6 +148,27 @@
    /**
     * Registers a boolean "enabled" property in this managed object.
     * When all the registered properties are true, the enabled
     * property in the aggregated managed object must also be true.
     * <p>
     * By default no source properties are defined which indicates
     * that the target property must always be true. When there is one
     * or more source properties defined, a target property must also
     * be defined.
     *
     * @param sourceEnabledPropertyName
     *          The optional boolean "enabled" property in this
     *          managed object.
     */
    public final void addSourceEnabledPropertyName(
        String sourceEnabledPropertyName) {
      this.sourceEnabledPropertyNames.add(sourceEnabledPropertyName);
    }
    /**
     * Sets the name of the managed object which is the parent of the
     * aggregated managed objects.
     * <p>
@@ -178,27 +204,6 @@
    /**
     * Registers a boolean "enabled" property in this managed object.
     * When all the registered properties are true, the enabled
     * property in the aggregated managed object must also be true.
     * <p>
     * By default no source properties are defined which indicates
     * that the target property must always be true. When there is one
     * or more source properties defined, a target property must also
     * be defined.
     *
     * @param sourceEnabledPropertyName
     *          The optional boolean "enabled" property in this
     *          managed object.
     */
    public final void addSourceEnabledPropertyName(
        String sourceEnabledPropertyName) {
      this.sourceEnabledPropertyNames.add(sourceEnabledPropertyName);
    }
    /**
     * Sets the optional boolean "enabled" property in the aggregated
     * managed object. This property must not be false while the
     * aggregated managed object is referenced.
@@ -240,8 +245,8 @@
      if (!sourceEnabledPropertyNames.isEmpty()
          && targetEnabledPropertyName == null) {
        throw new IllegalStateException(
            "One or more source properties defined but " +
            "target property is undefined");
            "One or more source properties defined but "
                + "target property is undefined");
      }
      return new AggregationPropertyDefinition<C, S>(d, propertyName, options,
@@ -393,42 +398,31 @@
   */
  private class ServerHandler extends ServerConstraintHandler {
    // The associated property definition.
    private final AggregationPropertyDefinition<C, S> pd;
    // Creates a new server-side constraint handler.
    private ServerHandler(AggregationPropertyDefinition<C, S> pd) {
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isUsable(ServerManagedObject<?> managedObject,
        Collection<Message> unacceptableReasons) throws ConfigException {
      SortedSet<String> names = managedObject.getPropertyValues(pd);
      SortedSet<String> names = managedObject
          .getPropertyValues(AggregationPropertyDefinition.this);
      ServerManagementContext context = ServerManagementContext.getInstance();
      BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
      List<BooleanPropertyDefinition> spdlist = pd
          .getSourceEnabledPropertyDefinitions();
      BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
      List<BooleanPropertyDefinition> spdlist =
        getSourceEnabledPropertyDefinitions();
      Message thisUFN = managedObject.getManagedObjectDefinition()
          .getUserFriendlyName();
      String thisDN = managedObject.getDN().toString();
      Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
      Message thatUFN = getRelationDefinition().getUserFriendlyName();
      boolean isUsable = true;
      for (String name : names) {
        ManagedObjectPath<C, S> path = pd.getChildPath(name);
        ManagedObjectPath<C, S> path = getChildPath(name);
        String thatDN = path.toDN().toString();
        if (!context.managedObjectExists(path)) {
          Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name, pd
              .getName(), thisUFN, thisDN, thatUFN, thatDN);
          Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name,
              getName(), thisUFN, thisDN, thatUFN, thatDN);
          unacceptableReasons.add(msg);
          isUsable = false;
        } else if (tpd != null) {
@@ -448,15 +442,15 @@
            if (isRequired && !ref.getPropertyValue(tpd)) {
              Message msg = ERR_SERVER_REFINT_SOURCE_ENABLED_TARGET_DISABLED
                  .get(name, pd.getName(), thisUFN, thisDN, thatUFN, thatDN);
                  .get(name, getName(), thisUFN, thisDN, thatUFN, thatDN);
              unacceptableReasons.add(msg);
              isUsable = false;
            }
          } else {
            // Target must always be enabled.
            if (!ref.getPropertyValue(tpd)) {
              Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name, pd
                  .getName(), thisUFN, thisDN, thatUFN, thatDN);
              Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name,
                  getName(), thisUFN, thisDN, thatUFN, thatDN);
              unacceptableReasons.add(msg);
              isUsable = false;
            }
@@ -484,13 +478,13 @@
      // Add change and delete listeners against all referenced
      // components.
      BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
      List<BooleanPropertyDefinition> spdlist = pd
          .getSourceEnabledPropertyDefinitions();
      BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
      List<BooleanPropertyDefinition> spdlist =
        getSourceEnabledPropertyDefinitions();
      Message thisUFN = managedObject.getManagedObjectDefinition()
          .getUserFriendlyName();
      String thisDN = managedObject.getDN().toString();
      Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
      Message thatUFN = getRelationDefinition().getUserFriendlyName();
      // Referenced managed objects will only need a change listener
      // if they have can be disabled.
@@ -510,7 +504,7 @@
      // Delete listeners need to be registered against the parent
      // entry of the referenced components.
      ServerManagementContext context = ServerManagementContext.getInstance();
      ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
      ManagedObjectPath<?, ?> parentPath = getParentPath();
      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
      // Create entries in the listener tables.
@@ -522,24 +516,25 @@
        new LinkedList<ReferentialIntegrityChangeListener>();
      changeListeners.put(managedObject.getDN(), clist);
      for (String name : managedObject.getPropertyValues(pd)) {
        ManagedObjectPath<C, S> path = pd.getChildPath(name);
      for (String name : managedObject
          .getPropertyValues(AggregationPropertyDefinition.this)) {
        ManagedObjectPath<C, S> path = getChildPath(name);
        DN dn = path.toDN();
        String thatDN = dn.toString();
        // Register the delete listener.
        Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN, pd
            .getName(), thisUFN, thisDN);
        Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN,
            getName(), thisUFN, thisDN);
        ReferentialIntegrityDeleteListener dl =
          new ReferentialIntegrityDeleteListener(dn, msg);
        parent.registerDeleteListener(pd.getRelationDefinition(), dl);
        parent.registerDeleteListener(getRelationDefinition(), dl);
        dlist.add(dl);
        // Register the change listener if required.
        if (needsChangeListeners) {
          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
          msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN, pd
              .getName(), thisUFN, thisDN);
          msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN,
              getName(), thisUFN, thisDN);
          ReferentialIntegrityChangeListener cl =
            new ReferentialIntegrityChangeListener(path, msg);
          ref.registerChangeListener(cl);
@@ -562,11 +557,11 @@
      // Delete listeners need to be deregistered against the parent
      // entry of the referenced components.
      ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
      ManagedObjectPath<?, ?> parentPath = getParentPath();
      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
      if (deleteListeners.containsKey(dn)) {
        for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
          parent.deregisterDeleteListener(pd.getRelationDefinition(), dl);
          parent.deregisterDeleteListener(getRelationDefinition(), dl);
        }
        deleteListeners.remove(dn);
      }
@@ -601,6 +596,323 @@
  /**
   * The client-side constraint handler implementation which enforces
   * referential integrity when aggregating managed objects are added
   * or modified.
   */
  private class SourceClientHandler extends ClientConstraintHandler {
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isAddAcceptable(ManagementContext context,
        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
        throws AuthorizationException, CommunicationException {
      // If all of this managed object's "enabled" properties are true
      // then any referenced managed objects must also be enabled.
      boolean needsEnabling = true;
      for (BooleanPropertyDefinition spd :
        getSourceEnabledPropertyDefinitions()) {
        if (!managedObject.getPropertyValue(spd)) {
          needsEnabling = false;
        }
      }
      // Check the referenced managed objects exist and, if required,
      // are enabled.
      boolean isAcceptable = true;
      BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
      Message ufn = getRelationDefinition().getUserFriendlyName();
      for (String name : managedObject
          .getPropertyValues(AggregationPropertyDefinition.this)) {
        // Retrieve the referenced managed object and make sure it
        // exists.
        ManagedObjectPath<?, ?> path = getChildPath(name);
        ManagedObject<?> ref;
        try {
          ref = context.getManagedObject(path);
        } catch (DefinitionDecodingException e) {
          Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name,
              getName(), e.getMessageObject());
          unacceptableReasons.add(msg);
          isAcceptable = false;
          continue;
        } catch (ManagedObjectDecodingException e) {
          Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name,
              getName(), e.getMessageObject());
          unacceptableReasons.add(msg);
          isAcceptable = false;
          continue;
        } catch (ManagedObjectNotFoundException e) {
          Message msg = ERR_CLIENT_REFINT_TARGET_DANGLING_REFERENCE.get(ufn,
              name, getName());
          unacceptableReasons.add(msg);
          isAcceptable = false;
          continue;
        }
        // Make sure the reference managed object is enabled.
        if (tpd != null && needsEnabling) {
          if (!ref.getPropertyValue(tpd)) {
            Message msg = ERR_CLIENT_REFINT_TARGET_DISABLED.get(ufn, name,
                getName());
            unacceptableReasons.add(msg);
            isAcceptable = false;
          }
        }
      }
      return isAcceptable;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isModifyAcceptable(ManagementContext context,
        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
        throws AuthorizationException, CommunicationException {
      // The same constraint applies as for adds.
      return isAddAcceptable(context, managedObject, unacceptableReasons);
    }
  }
  /**
   * The client-side constraint handler implementation which enforces
   * referential integrity when aggregated managed objects are deleted
   * or modified.
   */
  private class TargetClientHandler extends ClientConstraintHandler {
    /**
     * Instances of this class are used to search for all managed
     * objects that contain a reference to the named managed object.
     */
    private class Finder implements
        RelationDefinitionVisitor<Void, ManagedObject<?>> {
      // Any authorization exceptions that were encountered.
      private AuthorizationException ae = null;
      // Any communication exceptions that were encountered.
      private CommunicationException ce = null;
      // The name of the managed object being deleted or modified.
      private final String name;
      // The collected list of referencing managed objects.
      private final Collection<ManagedObject<?>> references;
      // Private constructor.
      private Finder(String name, Collection<ManagedObject<?>> references) {
        this.name = name;
        this.references = references;
      }
      /**
       * {@inheritDoc}
       */
      public Void visitInstantiable(InstantiableRelationDefinition<?, ?> rd,
          ManagedObject<?> p) {
        try {
          for (String childName : p.listChildren(rd)) {
            find(p.getChild(rd, childName));
          }
        } catch (AuthorizationException e) {
          ae = e;
        } catch (CommunicationException e) {
          ce = e;
        } catch (OperationsException e) {
          // Ignore all other types of exception.
        }
        return null;
      }
      /**
       * {@inheritDoc}
       */
      public Void visitOptional(OptionalRelationDefinition<?, ?> rd,
          ManagedObject<?> p) {
        try {
          find(p.getChild(rd));
        } catch (AuthorizationException e) {
          ae = e;
        } catch (CommunicationException e) {
          ce = e;
        } catch (OperationsException e) {
          // Ignore all other types of exception.
        }
        return null;
      }
      /**
       * {@inheritDoc}
       */
      public Void visitSingleton(SingletonRelationDefinition<?, ?> rd,
          ManagedObject<?> p) {
        try {
          find(p.getChild(rd));
        } catch (AuthorizationException e) {
          ae = e;
        } catch (CommunicationException e) {
          ce = e;
        } catch (OperationsException e) {
          // Ignore all other types of exception.
        }
        return null;
      }
      private void find(ManagedObject<?> current)
          throws AuthorizationException, CommunicationException {
        // First check the current managed object to see if it
        // contains a reference.
        ManagedObjectDefinition<?, ?> mod = current
            .getManagedObjectDefinition();
        if (mod.isChildOf(getManagedObjectDefinition())) {
          for (String value : current
              .getPropertyValues(AggregationPropertyDefinition.this)) {
            if (compare(value, name) == 0) {
              references.add(current);
            }
          }
        }
        // Now check its children.
        for (RelationDefinition<?, ?> rd : mod.getAllRelationDefinitions()) {
          rd.accept(this, current);
          if (ae != null) {
            throw ae;
          }
          if (ce != null) {
            throw ce;
          }
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isDeleteAcceptable(ManagementContext context,
        ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons)
        throws AuthorizationException, CommunicationException {
      // Any references to the deleted managed object should cause a
      // constraint violation.
      boolean isAcceptable = true;
      for (ManagedObject<?> mo : findReferences(context, path.getName())) {
        String name = mo.getManagedObjectPath().getName();
        if (name == null) {
          Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITHOUT_NAME.get(
              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
              getManagedObjectDefinition().getUserFriendlyName());
          unacceptableReasons.add(msg);
        } else {
          Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITH_NAME.get(
              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
              name, getManagedObjectDefinition().getUserFriendlyName());
          unacceptableReasons.add(msg);
        }
        isAcceptable = false;
      }
      return isAcceptable;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isModifyAcceptable(ManagementContext context,
        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
        throws AuthorizationException, CommunicationException {
      // If the modified managed object is disabled and there are some
      // active references then refuse the change.
      BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
      // The referenced managed object cannot be disabled: always ok.
      if (tpd == null) {
        return true;
      }
      // The referenced managed object is enabled: always ok.
      if (managedObject.getPropertyValue(tpd)) {
        return true;
      }
      // The referenced managed object is disabled. Need to check for
      // active references.
      boolean isAcceptable = true;
      for (ManagedObject<?> mo : findReferences(context, managedObject
          .getManagedObjectPath().getName())) {
        boolean needsEnabling = true;
        for (BooleanPropertyDefinition spd :
          getSourceEnabledPropertyDefinitions()) {
          if (!mo.getPropertyValue(spd)) {
            needsEnabling = false;
            break;
          }
        }
        if (needsEnabling) {
          String name = mo.getManagedObjectPath().getName();
          if (name == null) {
            Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITHOUT_NAME.get(
                managedObject.getManagedObjectDefinition()
                    .getUserFriendlyName(), getName(), mo
                    .getManagedObjectDefinition().getUserFriendlyName());
            unacceptableReasons.add(msg);
          } else {
            Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITH_NAME.get(
                managedObject.getManagedObjectDefinition()
                    .getUserFriendlyName(), getName(), mo
                    .getManagedObjectDefinition().getUserFriendlyName(), name);
            unacceptableReasons.add(msg);
          }
          isAcceptable = false;
        }
      }
      return isAcceptable;
    }
    // Find all managed objects which reference the named managed
    // object using this property.
    private Collection<ManagedObject<?>> findReferences(
        ManagementContext context, String name) throws AuthorizationException,
        CommunicationException {
      List<ManagedObject<?>> references = new LinkedList<ManagedObject<?>>();
      Finder finder = new Finder(name, references);
      finder.find(context.getRootConfigurationManagedObject());
      return references;
    }
  }
  /**
   * Creates an aggregation property definition builder.
   *
   * @param <C>
@@ -760,8 +1072,8 @@
   * {@inheritDoc}
   */
  public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
    // TODO: not yet implemented.
    return Collections.emptyList();
    ClientConstraintHandler handler = new SourceClientHandler();
    return Collections.singleton(handler);
  }
@@ -796,7 +1108,7 @@
   * {@inheritDoc}
   */
  public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
    ServerConstraintHandler handler = new ServerHandler(this);
    ServerConstraintHandler handler = new ServerHandler();
    return Collections.singleton(handler);
  }
@@ -923,18 +1235,43 @@
      sourceEnabledProperties.add(BooleanPropertyDefinition.class.cast(pd));
    }
    d = relationDefinition.getChildDefinition();
    if (targetEnabledPropertyName == null) {
      targetEnabledProperty = null;
    } else {
      PropertyDefinition<?> pd;
      d = relationDefinition.getChildDefinition();
      pd = d.getPropertyDefinition(targetEnabledPropertyName);
      PropertyDefinition<?> pd = d
          .getPropertyDefinition(targetEnabledPropertyName);
      // Runtime cast is required to workaround a
      // bug in JDK versions prior to 1.5.0_08.
      targetEnabledProperty = BooleanPropertyDefinition.class.cast(pd);
    }
    // Register a client-side constraint with the referenced
    // definition. This will be used to enforce referential integrity
    // for actions performed against referenced managed objects.
    Constraint constraint = new Constraint() {
      /**
       * {@inheritDoc}
       */
      public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
        ClientConstraintHandler handler = new TargetClientHandler();
        return Collections.singleton(handler);
      }
      /**
       * {@inheritDoc}
       */
      public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
        return Collections.emptyList();
      }
    };
    d.registerConstraint(constraint);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/AggregationTest.java
@@ -134,6 +134,45 @@
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=bad rdn, cn=config",
      "",
      "dn: cn=Connection Handlers,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-branch",
      "cn: Connection Handlers",
      "",
      "dn: cn=LDAP Connection Handler,cn=Connection Handlers,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-connection-handler",
      "objectClass: ds-cfg-ldap-connection-handler",
      "cn: LDAP Connection Handler",
      "ds-cfg-connection-handler-class: org.opends.server.protocols.ldap.LDAPConnectionHandler",
      "ds-cfg-connection-handler-enabled: true",
      "ds-cfg-listen-address: 0.0.0.0",
      "ds-cfg-listen-port: 389",
      "",
      "dn: cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-connection-handler",
      "objectClass: ds-cfg-ldap-connection-handler",
      "cn: LDAPS Connection Handler",
      "ds-cfg-connection-handler-class: org.opends.server.protocols.ldap.LDAPConnectionHandler",
      "ds-cfg-connection-handler-enabled: false",
      "ds-cfg-listen-address: 0.0.0.0",
      "ds-cfg-listen-port: 636",
      "ds-cfg-use-ssl: true",
      "ds-cfg-ssl-client-auth-policy: optional",
      "ds-cfg-ssl-cert-nickname: server-cert",
      "ds-cfg-key-manager-provider-dn: cn=JKS,cn=Key Manager Providers,cn=config",
      "ds-cfg-trust-manager-provider-dn: cn=JKS,cn=Trust Manager Providers,cn=config",
      "",
      "dn: cn=JMX Connection Handler,cn=Connection Handlers,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-connection-handler",
      "objectClass: ds-cfg-jmx-connection-handler",
      "cn: JMX Connection Handler",
      "ds-cfg-connection-handler-class: org.opends.server.protocols.jmx.JmxConnectionHandler",
      "ds-cfg-connection-handler-enabled: false",
      "ds-cfg-listen-port: 1689",
      ""
  };