From 98a7494fc691c5a5af0bfac786fad827af567746 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Tue, 03 Feb 2015 11:02:53 +0000
Subject: [PATCH] OPENDJ-1242 (CR-5940) Enable dsconfig to generate doc for properties changed through subcommand options
---
opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java | 385 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 384 insertions(+), 1 deletions(-)
diff --git a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
index e8fcc50..69e35ac 100644
--- a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
+++ b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2007-2010 Sun Microsystems, Inc.
- * Portions Copyright 2012-2014 ForgeRock AS
+ * Portions Copyright 2012-2015 ForgeRock AS
*/
package org.forgerock.opendj.config.dsconfig;
@@ -32,6 +32,7 @@
import static com.forgerock.opendj.dsconfig.DsconfigMessages.*;
import static com.forgerock.opendj.util.StaticUtils.*;
+import static org.forgerock.opendj.config.PropertyOption.*;
import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.*;
import static org.forgerock.util.Utils.*;
@@ -51,6 +52,7 @@
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -60,15 +62,43 @@
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.config.ACIPropertyDefinition;
+import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider;
+import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
+import org.forgerock.opendj.config.AdministratorAction;
+import org.forgerock.opendj.config.AdministratorAction.Type;
+import org.forgerock.opendj.config.AggregationPropertyDefinition;
+import org.forgerock.opendj.config.AliasDefaultBehaviorProvider;
+import org.forgerock.opendj.config.AttributeTypePropertyDefinition;
+import org.forgerock.opendj.config.BooleanPropertyDefinition;
+import org.forgerock.opendj.config.ClassPropertyDefinition;
import org.forgerock.opendj.config.ConfigurationFramework;
+import org.forgerock.opendj.config.DNPropertyDefinition;
+import org.forgerock.opendj.config.DefaultBehaviorProvider;
+import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider;
+import org.forgerock.opendj.config.DurationPropertyDefinition;
+import org.forgerock.opendj.config.DurationUnit;
+import org.forgerock.opendj.config.EnumPropertyDefinition;
+import org.forgerock.opendj.config.IPAddressMaskPropertyDefinition;
+import org.forgerock.opendj.config.IPAddressPropertyDefinition;
import org.forgerock.opendj.config.InstantiableRelationDefinition;
+import org.forgerock.opendj.config.IntegerPropertyDefinition;
+import org.forgerock.opendj.config.PropertyDefinition;
+import org.forgerock.opendj.config.PropertyDefinitionVisitor;
+import org.forgerock.opendj.config.PropertyOption;
import org.forgerock.opendj.config.RelationDefinition;
+import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider;
import org.forgerock.opendj.config.SetRelationDefinition;
+import org.forgerock.opendj.config.SizePropertyDefinition;
+import org.forgerock.opendj.config.StringPropertyDefinition;
import org.forgerock.opendj.config.Tag;
+import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider;
import org.forgerock.opendj.config.client.ManagedObjectDecodingException;
import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException;
import org.forgerock.opendj.config.client.OperationRejectedException;
import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.util.Utils;
import com.forgerock.opendj.cli.ArgumentException;
import com.forgerock.opendj.cli.ArgumentGroup;
@@ -87,6 +117,7 @@
import com.forgerock.opendj.cli.StringArgument;
import com.forgerock.opendj.cli.SubCommand;
import com.forgerock.opendj.cli.SubCommandArgumentParser;
+import com.forgerock.opendj.cli.SubCommandUsageHandler;
import com.forgerock.opendj.cli.VersionHandler;
/**
@@ -94,6 +125,355 @@
*/
public final class DSConfig extends ConsoleApplication {
+ // FIXME: I18n support. Today all the strings are hardcoded in this file
+ private final class DSConfigSubCommandUsageHandler implements SubCommandUsageHandler {
+
+ private static final String ALLOW_UNLIMITED = "A value of \"-1\" or \"unlimited\" for no limit.";
+ private static final String ACI_SYNTAX_REL_URL =
+ "<link"
+ + " xlink:show=\"new\""
+ + " xlink:href=\"admin-guide#about-acis\""
+ + " xlink:role=\"http://docbook.org/xlink/role/olink\">"
+ + "<citetitle>About Access Control Instructions</citetitle></link>";
+ private static final String DURATION_SYNTAX_REL_URL =
+ " <itemizedlist>"
+ + " <para>Some property values take a time duration. Durations are expressed"
+ + " as numbers followed by units. For example <literal>1 s</literal> means"
+ + " one second, and <literal>2 w</literal> means two weeks. Some durations"
+ + " have minimum granularity or maximum units, so you cannot necessary specify"
+ + " every duration in milliseconds or weeks for example. Some durations allow"
+ + " you to use a special value to mean unlimited. Units are specified as"
+ + " follows.</para>"
+ + " <listitem><para><literal>ms</literal>: milliseconds</para></listitem>"
+ + " <listitem><para><literal>s</literal>: seconds</para></listitem>"
+ + " <listitem><para><literal>m</literal>: minutes</para></listitem>"
+ + " <listitem><para><literal>h</literal>: hours</para></listitem>"
+ + " <listitem><para><literal>d</literal>: days</para></listitem>"
+ + " <listitem><para><literal>w</literal>: weeks</para></listitem>"
+ + " </itemizedlist>";
+
+ /** {@inheritDoc} */
+ @Override
+ public void appendUsage(StringBuilder sb, SubCommand sc, String argLongID) {
+ final String toolName = "dsconfig";
+ final SubCommandHandler sch = handlers.get(sc);
+ final RelationDefinition<?, ?> rd = getRelationDefinition(sch);
+ if (rd instanceof InstantiableRelationDefinition) {
+ final PropertyDefinition<?> pd =
+ ((InstantiableRelationDefinition<?, ?>) rd).getNamingPropertyDefinition();
+ if (pd != null) {
+ final AbstractManagedObjectDefinition<?, ?> defn = pd.getManagedObjectDefinition();
+ final List<PropertyDefinition<?>> props =
+ new ArrayList<PropertyDefinition<?>>(defn.getAllPropertyDefinitions());
+ Collections.sort(props);
+ final String propPrefix = toolName + "-" + sc.getName() + "-" + argLongID + "-";
+ sb.append(EOL);
+ toSimpleList(props, propPrefix, sb);
+ sb.append(EOL);
+ toVariableList(props, defn, propPrefix, sb);
+ }
+ }
+ }
+
+ private RelationDefinition<?, ?> getRelationDefinition(final SubCommandHandler sch) {
+ if (sch instanceof CreateSubCommandHandler) {
+ return ((CreateSubCommandHandler<?, ?>) sch).getRelationDefinition();
+ } else if (sch instanceof DeleteSubCommandHandler) {
+ return ((DeleteSubCommandHandler) sch).getRelationDefinition();
+ } else if (sch instanceof ListSubCommandHandler) {
+ return ((ListSubCommandHandler) sch).getRelationDefinition();
+ } else if (sch instanceof GetPropSubCommandHandler) {
+ return ((GetPropSubCommandHandler) sch).getRelationDefinition();
+ } else if (sch instanceof SetPropSubCommandHandler) {
+ return ((SetPropSubCommandHandler) sch).getRelationDefinition();
+ }
+ return null;
+ }
+
+ private void toSimpleList(List<PropertyDefinition<?>> props, String propPrefix, StringBuilder b) {
+ b.append(" <simplelist>").append(EOL);
+ for (PropertyDefinition<?> prop : props) {
+ b.append(" <member><xref linkend=\"")
+ .append(propPrefix).append(prop.getName()).append("\" /></member>").append(EOL);
+ }
+ b.append(" </simplelist>").append(EOL);
+ }
+
+ private void toVariableList(List<PropertyDefinition<?>> props, AbstractManagedObjectDefinition<?, ?> defn,
+ String propPrefix, StringBuilder b) {
+ final String indent = " ";
+ b.append(" <variablelist>").append(EOL);
+ for (PropertyDefinition<?> prop : props) {
+ b.append(" <varlistentry xml:id=\"")
+ .append(propPrefix).append(prop.getName()).append("\">").append(EOL);
+ b.append(" <term>").append(prop.getName()).append("</term>").append(EOL);
+ b.append(" <listitem>").append(EOL);
+ b.append(" <variablelist>").append(EOL);
+ appendVarlistentry(b, "Description", getDescriptionString(prop), indent);
+ appendVarlistentry(b, "Default Value", getDefaultBehaviorString(prop), indent);
+ appendAllowedValues(b, prop, indent);
+ appendVarlistentry(b, "Multi-valued", getYN(prop, MULTI_VALUED), indent);
+ appendVarlistentry(b, "Required", getYN(prop, MANDATORY), indent);
+ appendVarlistentry(b, "Admin Action Required", getAdminActionRequired(prop, defn), indent);
+ appendVarlistentry(b, "Advanced Property", getYNAdvanced(prop, ADVANCED), indent);
+ appendVarlistentry(b, "Read-only", getYN(prop, READ_ONLY), indent);
+ b.append(" </variablelist>").append(EOL);
+ b.append(" </listitem>").append(EOL);
+ b.append(" </varlistentry>").append(EOL);
+ }
+ b.append(" </variablelist>").append(EOL);
+ }
+
+ private StringBuilder appendVarlistentry(StringBuilder b, String term, Object para, String indent) {
+ b.append(indent).append("<varlistentry>").append(EOL);
+ b.append(indent).append(" <term>").append(term).append("</term>").append(EOL);
+ b.append(indent).append(" <listitem>").append(EOL);
+ b.append(indent).append(" <para>").append(para).append("</para>").append(EOL);
+ b.append(indent).append(" </listitem>").append(EOL);
+ b.append(indent).append("</varlistentry>").append(EOL);
+ return b;
+ }
+
+ private void appendAllowedValues(StringBuilder b, PropertyDefinition<?> prop, String indent) {
+ b.append(indent).append("<varlistentry>").append(EOL);
+ b.append(indent).append(" <term>").append("Allowed Values").append("</term>").append(EOL);
+ if (prop instanceof EnumPropertyDefinition) {
+ b.append(indent).append(" <listitem>").append(EOL);
+ b.append(indent).append(" <variablelist>").append(EOL);
+ appendSyntax(b, prop, indent + " ");
+ b.append(indent).append(" </variablelist>").append(EOL);
+ b.append(indent).append(" </listitem>").append(EOL);
+ } else if (prop instanceof BooleanPropertyDefinition) {
+ b.append(indent).append(" <listitem><para>true</para></listitem>").append(EOL);
+ b.append(indent).append(" <listitem><para>false</para></listitem>").append(EOL);
+ } else {
+ b.append(indent).append(" <listitem>").append(EOL);
+ b.append(indent).append(" <para>");
+ appendSyntax(b, prop, indent);
+ b.append("</para>").append(EOL);
+ b.append(indent).append(" </listitem>").append(EOL);
+ }
+ b.append(indent).append("</varlistentry>").append(EOL);
+ }
+
+ private Object getDescriptionString(PropertyDefinition<?> prop) {
+ return ((prop.getSynopsis() != null) ? prop.getSynopsis() + " " : "")
+ + ((prop.getDescription() != null) ? prop.getDescription() : "");
+ }
+
+ private String getAdminActionRequired(PropertyDefinition<?> prop, AbstractManagedObjectDefinition<?, ?> defn) {
+ final AdministratorAction adminAction = prop.getAdministratorAction();
+ if (adminAction != null) {
+ final LocalizableMessage synopsis = adminAction.getSynopsis();
+ final Type actionType = adminAction.getType();
+ final StringBuilder action = new StringBuilder();
+ if (actionType == Type.COMPONENT_RESTART) {
+ action.append("The ").append(defn.getUserFriendlyName())
+ .append(" must be disabled and re-enabled for changes to this setting to take effect");
+ } else if (actionType == Type.SERVER_RESTART) {
+ action.append("Restart the server");
+ } else if (actionType == Type.NONE) {
+ action.append("None");
+ }
+ if (synopsis != null) {
+ if (action.length() > 0) {
+ action.append(". ");
+ }
+ action.append(synopsis);
+ }
+ return action.toString();
+ }
+ return "None";
+ }
+
+ private String getYN(PropertyDefinition<?> prop, PropertyOption option) {
+ return prop.hasOption(option) ? "Yes" : "No";
+ }
+
+ private String getYNAdvanced(PropertyDefinition<?> prop, PropertyOption option) {
+ return prop.hasOption(option) ? "Yes (Use --advanced in interactive mode.)" : "No";
+ }
+
+ private String getDefaultBehaviorString(PropertyDefinition<?> prop) {
+ DefaultBehaviorProvider<?> defaultBehavior = prop.getDefaultBehaviorProvider();
+ if (defaultBehavior instanceof UndefinedDefaultBehaviorProvider) {
+ return "None";
+ } else if (defaultBehavior instanceof DefinedDefaultBehaviorProvider) {
+ DefinedDefaultBehaviorProvider<?> behavior = (DefinedDefaultBehaviorProvider<?>) defaultBehavior;
+ final StringBuilder res = new StringBuilder();
+ for (Iterator<String> it = behavior.getDefaultValues().iterator(); it.hasNext();) {
+ String str = it.next();
+ res.append(str).append(it.hasNext() ? "\n" : "");
+ }
+ return res.toString();
+ } else if (defaultBehavior instanceof AliasDefaultBehaviorProvider) {
+ AliasDefaultBehaviorProvider<?> behavior = (AliasDefaultBehaviorProvider<?>) defaultBehavior;
+ return behavior.getSynopsis().toString();
+ } else if (defaultBehavior instanceof RelativeInheritedDefaultBehaviorProvider) {
+ final RelativeInheritedDefaultBehaviorProvider<?> behavior =
+ (RelativeInheritedDefaultBehaviorProvider<?>) defaultBehavior;
+ return getDefaultBehaviorString(
+ behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
+ } else if (defaultBehavior instanceof AbsoluteInheritedDefaultBehaviorProvider) {
+ final AbsoluteInheritedDefaultBehaviorProvider<?> behavior =
+ (AbsoluteInheritedDefaultBehaviorProvider<?>) defaultBehavior;
+ return getDefaultBehaviorString(
+ behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
+ }
+ return "";
+ }
+
+ private void appendSyntax(final StringBuilder b, PropertyDefinition<?> prop, final String indent) {
+ // Create a visitor for performing syntax specific processing.
+ PropertyDefinitionVisitor<String, Void> visitor = new PropertyDefinitionVisitor<String, Void>() {
+
+ @Override
+ public String visitACI(ACIPropertyDefinition prop, Void p) {
+ b.append(ACI_SYNTAX_REL_URL);
+ return null;
+ }
+
+ @Override
+ public String visitAggregation(AggregationPropertyDefinition prop, Void p) {
+ final RelationDefinition<?, ?> rel = prop.getRelationDefinition();
+ final String linkStr = getLink(rel.getName() + ".html");
+ b.append("The DN of any ").append(linkStr).append(". ");
+ final LocalizableMessage synopsis = prop.getSourceConstraintSynopsis();
+ if (synopsis != null) {
+ b.append(synopsis);
+ }
+ return null;
+ }
+
+ @Override
+ public String visitAttributeType(AttributeTypePropertyDefinition prop, Void p) {
+ b.append("The name of an attribute type defined in the server schema.");
+ return null;
+ }
+
+ @Override
+ public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
+ throw new RuntimeException("This case should be handled by the calling code.");
+ }
+
+ @Override
+ public String visitClass(ClassPropertyDefinition prop, Void p) {
+ b.append("A java class that implements or extends the class(es) :")
+ .append(Utils.joinAsString(EOL, prop.getInstanceOfInterface()));
+ return null;
+ }
+
+ @Override
+ public String visitDN(DNPropertyDefinition prop, Void p) {
+ final DN baseDN = prop.getBaseDN();
+ b.append("A valid DN.");
+ if (baseDN != null) {
+ b.append(baseDN);
+ }
+ return null;
+ }
+
+ @Override
+ public String visitDuration(DurationPropertyDefinition prop, Void p) {
+ b.append(DURATION_SYNTAX_REL_URL).append(". ");
+ if (prop.isAllowUnlimited()) {
+ b.append(ALLOW_UNLIMITED).append(" ");
+ }
+ if (prop.getMaximumUnit() != null) {
+ b.append("Maximum unit is \"").append(prop.getMaximumUnit().getLongName()).append("\". ");
+ }
+ final DurationUnit baseUnit = prop.getBaseUnit();
+ b.append("Lower limit is ").append(valueOf(baseUnit, prop.getLowerLimit()))
+ .append(" ").append(baseUnit.getLongName()).append(". ");
+ if (prop.getUpperLimit() != null) {
+ b.append("Upper limit is ").append(valueOf(baseUnit, prop.getUpperLimit()))
+ .append(" ").append(baseUnit.getLongName()).append(". ");
+ }
+ return null;
+ }
+
+ private long valueOf(final DurationUnit baseUnit, long upperLimit) {
+ return Double.valueOf(baseUnit.fromMilliSeconds(upperLimit)).longValue();
+ }
+
+ @Override
+ public String visitEnum(EnumPropertyDefinition prop, Void p) {
+ final Class<?> en = prop.getEnumClass();
+ final Object[] constants = en.getEnumConstants();
+ for (Object enumConstant : constants) {
+ final LocalizableMessage valueSynopsis = prop.getValueSynopsis((Enum) enumConstant);
+ appendVarlistentry(b, enumConstant.toString(), valueSynopsis, indent);
+ }
+ return null;
+ }
+
+ @Override
+ public String visitInteger(IntegerPropertyDefinition prop, Void p) {
+ b.append("An integer value. Lower value is ").append(prop.getLowerLimit()).append(".");
+ if (prop.getUpperLimit() != null) {
+ b.append(" Upper value is ").append(prop.getUpperLimit()).append(".");
+ }
+ if (prop.isAllowUnlimited()) {
+ b.append(" ").append(ALLOW_UNLIMITED);
+ }
+ if (prop.getUnitSynopsis() != null) {
+ b.append(" Unit is ").append(prop.getUnitSynopsis()).append(".");
+ }
+ return null;
+ }
+
+ @Override
+ public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
+ b.append("An IP address");
+ return null;
+ }
+
+ @Override
+ public String visitIPAddressMask(IPAddressMaskPropertyDefinition prop, Void p) {
+ b.append("An IP address mask");
+ return null;
+ }
+
+ @Override
+ public String visitSize(SizePropertyDefinition prop, Void p) {
+ if (prop.getLowerLimit() != 0) {
+ b.append(" Lower value is ").append(prop.getLowerLimit()).append(".");
+ }
+ if (prop.getUpperLimit() != null) {
+ b.append(" Upper value is ").append(prop.getUpperLimit()).append(" .");
+ }
+ if (prop.isAllowUnlimited()) {
+ b.append(" ").append(ALLOW_UNLIMITED);
+ }
+ return null;
+ }
+
+ @Override
+ public String visitString(StringPropertyDefinition prop, Void p) {
+ if (prop.getPatternSynopsis() != null) {
+ b.append(prop.getPatternSynopsis());
+ } else {
+ b.append("A String");
+ }
+ return null;
+ }
+
+ @Override
+ public String visitUnknown(PropertyDefinition prop, Void p) {
+ b.append("Unknown");
+ return null;
+ }
+ };
+
+ // Invoke the visitor against the property definition.
+ prop.accept(visitor, null);
+ }
+
+ private String getLink(String target) {
+ return " <xref linkend=" + target + " />";
+ }
+ }
+
/** The name of this tool. */
static final String DSCONFIGTOOLNAME = "dsconfig";
@@ -423,6 +803,9 @@
return "";
}
});
+ if (System.getProperty("org.forgerock.opendj.gendoc") != null) {
+ this.parser.setUsageHandler(new DSConfigSubCommandUsageHandler());
+ }
}
/** {@inheritDoc} */
--
Gitblit v1.10.0