/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2012-2015 ForgeRock AS.
*/
package org.forgerock.opendj.config.dsconfig;
import static com.forgerock.opendj.cli.ArgumentConstants.*;
import static com.forgerock.opendj.cli.CliMessages.*;
import static com.forgerock.opendj.cli.DocGenerationHelper.*;
import static com.forgerock.opendj.cli.Utils.*;
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.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1;
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.Argument;
import com.forgerock.opendj.cli.ArgumentException;
import com.forgerock.opendj.cli.ArgumentGroup;
import com.forgerock.opendj.cli.BooleanArgument;
import com.forgerock.opendj.cli.CliConstants;
import com.forgerock.opendj.cli.ClientException;
import com.forgerock.opendj.cli.CommandBuilder;
import com.forgerock.opendj.cli.CommonArguments;
import com.forgerock.opendj.cli.ConnectionFactoryProvider;
import com.forgerock.opendj.cli.ConsoleApplication;
import com.forgerock.opendj.cli.Menu;
import com.forgerock.opendj.cli.MenuBuilder;
import com.forgerock.opendj.cli.MenuCallback;
import com.forgerock.opendj.cli.MenuResult;
import com.forgerock.opendj.cli.ReturnCode;
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;
/**
* This class provides a command-line tool which enables administrators to configure the Directory Server.
*/
public final class DSConfig extends ConsoleApplication {
/**
* This class provides additional information about subcommands for generated reference documentation.
*/
private final class DSConfigSubCommandUsageHandler implements SubCommandUsageHandler {
/** Marker to open a DocBook XML paragraph. */
private String op = "";
/** Marker to close a DocBook XML paragraph. */
private String cp = " ";
/** {@inheritDoc} */
@Override
public String getArgumentAdditionalInfo(SubCommand sc, Argument a, String nameOption) {
StringBuilder sb = new StringBuilder();
final AbstractManagedObjectDefinition, ?> defn = getManagedObjectDefinition(sc);
if (defn == null) {
return "";
}
if (doesHandleProperties(a)) {
final LocalizableMessage name = defn.getUserFriendlyName();
sb.append(op).append(REF_DSCFG_ARG_ADDITIONAL_INFO.get(name, name, nameOption)).append(cp).append(EOL);
} else {
listSubtypes(sb, a, defn);
}
return sb.toString();
}
private void listSubtypes(StringBuilder sb, Argument a, AbstractManagedObjectDefinition, ?> defn) {
final LocalizableMessage placeholder = a.getValuePlaceholder();
Map map = new HashMap();
final LocalizableMessage name = defn.getUserFriendlyName();
map.put("dependencies", REF_DSCFG_SUBTYPE_DEPENDENCIES.get(name, name, placeholder));
map.put("typesIntro", REF_DSCFG_SUBTYPE_TYPES_INTRO.get(name));
List> children = new LinkedList>();
for (AbstractManagedObjectDefinition, ?> childDefn : getLeafChildren(defn)) {
Map child = new HashMap();
child.put("name", childDefn.getName());
child.put("default", REF_DSCFG_CHILD_DEFAULT.get(placeholder, childDefn.getUserFriendlyName()));
child.put("enabled", REF_DSCFG_CHILD_ENABLED_BY_DEFAULT.get(propertyExists(childDefn, "enabled")));
final String link = getLink(getScriptName() + "-" + a.getLongIdentifier()
+ "-" + defn.getName() + "-prop-" + childDefn.getName());
child.put("link", REF_DSCFG_CHILD_LINK.get(link, defn.getUserFriendlyName()));
children.add(child);
}
map.put("children", children);
applyTemplate(sb, "dscfgListSubtypes.ftl", map);
}
private boolean propertyExists(AbstractManagedObjectDefinition, ?> defn, String name) {
try {
return defn.getPropertyDefinition(name) != null;
} catch (IllegalArgumentException e) {
return false;
}
}
/** {@inheritDoc} */
@Override
public String getProperties(SubCommand sc) {
StringBuilder sb = new StringBuilder();
final AbstractManagedObjectDefinition, ?> defn = getManagedObjectDefinition(sc);
if (defn == null) {
return "";
}
for (AbstractManagedObjectDefinition, ?> childDefn : getLeafChildren(defn)) {
final List> props =
new ArrayList>(childDefn.getAllPropertyDefinitions());
Collections.sort(props);
Map map = new HashMap();
final String propPrefix = getScriptName() + "-" + sc.getName() + "-" + childDefn.getName();
map.put("id", propPrefix);
map.put("title", childDefn.getUserFriendlyName());
map.put("intro", REF_DSCFG_PROPS_INTRO.get(defn.getUserFriendlyPluralName(), childDefn.getName()));
map.put("list", toVariableList(props, defn, propPrefix));
applyTemplate(sb, "dscfgAppendProps.ftl", map);
}
return sb.toString();
}
private AbstractManagedObjectDefinition, ?> getManagedObjectDefinition(SubCommand sc) {
final SubCommandHandler sch = handlers.get(sc);
if (sch instanceof HelpSubCommandHandler) {
return null;
}
final RelationDefinition, ?> rd = getRelationDefinition(sch);
return rd.getChildDefinition();
}
private List> getLeafChildren(
AbstractManagedObjectDefinition, ?> defn) {
final ArrayList> results =
new ArrayList>();
addIfLeaf(results, defn);
Collections.sort(results, new Comparator>() {
@Override
public int compare(AbstractManagedObjectDefinition, ?> o1, AbstractManagedObjectDefinition, ?> o2) {
return o1.getName().compareTo(o2.getName());
}
});
return results;
}
private void addIfLeaf(final Collection> results,
final AbstractManagedObjectDefinition, ?> defn) {
if (defn.getChildren().isEmpty()) {
results.add(defn);
} else {
for (AbstractManagedObjectDefinition, ?> child : defn.getChildren()) {
addIfLeaf(results, child);
}
}
}
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 String toVariableList(List> props, AbstractManagedObjectDefinition, ?> defn,
String propPrefix) {
StringBuilder b = new StringBuilder();
Map map = new HashMap();
List> properties = new LinkedList>();
for (PropertyDefinition> prop : props) {
Map property = new HashMap();
property.put("id", propPrefix + "-" + prop.getName());
property.put("term", prop.getName());
property.put("descTitle", REF_TITLE_DESCRIPTION.get());
property.put("description", getDescriptionString(prop));
final StringBuilder sb = new StringBuilder();
appendDefaultBehavior(sb, prop);
appendAllowedValues(sb, prop);
appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_MULTI_VALUED.get().toString(), getYN(prop, MULTI_VALUED));
appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_REQUIRED.get().toString(), getYN(prop, MANDATORY));
appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_ADMIN_ACTION_REQUIRED.get().toString(),
getAdminActionRequired(prop, defn));
appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_ADVANCED_PROPERTY.get().toString(),
getYNAdvanced(prop, ADVANCED));
appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_READ_ONLY.get().toString(), getYN(prop, READ_ONLY));
property.put("list", sb.toString());
properties.add(property);
}
map.put("properties", properties);
applyTemplate(b, "dscfgVariableList.ftl", map);
return b.toString();
}
private StringBuilder appendVarListEntry(StringBuilder b, String term, Object definition) {
Map map = new HashMap();
map.put("term", term);
map.put("definition", definition);
applyTemplate(b, "dscfgVarListEntry.ftl", map);
return b;
}
private void appendDefaultBehavior(StringBuilder b, PropertyDefinition> prop) {
StringBuilder sb = new StringBuilder();
appendDefaultBehaviorString(sb, prop);
appendVarListEntry(b, REF_DSCFG_PROPS_LABEL_DEFAULT_VALUE.get().toString(), sb.toString());
}
private void appendAllowedValues(StringBuilder b, PropertyDefinition> prop) {
StringBuilder sb = new StringBuilder();
appendSyntax(sb, prop);
appendVarListEntry(b, REF_DSCFG_PROPS_LABEL_ALLOWED_VALUES.get().toString(), sb.toString());
}
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(op)
.append(REF_DSCFG_ADMIN_ACTION_COMPONENT_RESTART.get(defn.getUserFriendlyName()))
.append(cp);
} else if (actionType == Type.SERVER_RESTART) {
action.append(op).append(REF_DSCFG_ADMIN_ACTION_SERVER_RESTART.get()).append(cp);
} else if (actionType == Type.NONE) {
action.append(op).append(REF_DSCFG_ADMIN_ACTION_NONE.get()).append(cp);
}
if (synopsis != null) {
action.append(op).append(synopsis).append(cp);
}
return action.toString();
}
return op + REF_DSCFG_ADMIN_ACTION_NONE.get() + cp;
}
private String getYN(PropertyDefinition> prop, PropertyOption option) {
LocalizableMessage msg = prop.hasOption(option) ? REF_DSCFG_PROP_YES.get() : REF_DSCFG_PROP_NO.get();
return op + msg + cp;
}
private String getYNAdvanced(PropertyDefinition> prop, PropertyOption option) {
LocalizableMessage msg = prop.hasOption(option)
? REF_DSCFG_PROP_YES_ADVANCED.get() : REF_DSCFG_PROP_NO.get();
return op + msg + cp;
}
private void appendDefaultBehaviorString(StringBuilder b, PropertyDefinition> prop) {
final DefaultBehaviorProvider> defaultBehavior = prop.getDefaultBehaviorProvider();
if (defaultBehavior instanceof UndefinedDefaultBehaviorProvider) {
b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR_NONE.get()).append(cp).append(EOL);
} else if (defaultBehavior instanceof DefinedDefaultBehaviorProvider) {
DefinedDefaultBehaviorProvider> behavior = (DefinedDefaultBehaviorProvider>) defaultBehavior;
final Collection defaultValues = behavior.getDefaultValues();
if (defaultValues.size() == 0) {
b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR_NONE.get()).append(cp).append(EOL);
} else if (defaultValues.size() == 1) {
b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(defaultValues.iterator().next()))
.append(cp).append(EOL);
} else {
final Iterator it = defaultValues.iterator();
b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(it.next())).append(cp);
for (; it.hasNext();) {
b.append(EOL).append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(it.next())).append(cp);
}
b.append(EOL);
}
} else if (defaultBehavior instanceof AliasDefaultBehaviorProvider) {
AliasDefaultBehaviorProvider> behavior = (AliasDefaultBehaviorProvider>) defaultBehavior;
b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(behavior.getSynopsis())).append(cp).append(EOL);
} else if (defaultBehavior instanceof RelativeInheritedDefaultBehaviorProvider) {
final RelativeInheritedDefaultBehaviorProvider> behavior =
(RelativeInheritedDefaultBehaviorProvider>) defaultBehavior;
appendDefaultBehaviorString(b,
behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
} else if (defaultBehavior instanceof AbsoluteInheritedDefaultBehaviorProvider) {
final AbsoluteInheritedDefaultBehaviorProvider> behavior =
(AbsoluteInheritedDefaultBehaviorProvider>) defaultBehavior;
appendDefaultBehaviorString(b,
behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
}
}
private void appendSyntax(final StringBuilder b, PropertyDefinition> prop) {
// Create a visitor for performing syntax specific processing.
PropertyDefinitionVisitor visitor = new PropertyDefinitionVisitor() {
@Override
public String visitACI(ACIPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_ACI_SYNTAX_REL_URL.get()).append(cp).append(EOL);
return null;
}
@Override
public String visitAggregation(AggregationPropertyDefinition prop, Void p) {
b.append(op);
final RelationDefinition, ?> rel = prop.getRelationDefinition();
final String linkStr = getLink(rel.getName());
b.append(REF_DSCFG_AGGREGATION.get(linkStr)).append(". ");
final LocalizableMessage synopsis = prop.getSourceConstraintSynopsis();
if (synopsis != null) {
b.append(synopsis);
}
b.append(cp).append(EOL);
return null;
}
@Override
public String visitAttributeType(AttributeTypePropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_ANY_ATTRIBUTE.get()).append(".").append(cp).append(EOL);
return null;
}
@Override
public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
b.append(op).append("true").append(cp).append(EOL);
b.append(op).append("false").append(cp).append(EOL);
return null;
}
@Override
public String visitClass(ClassPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_JAVA_PLUGIN.get()).append(" ")
.append(Utils.joinAsString(EOL, prop.getInstanceOfInterface())).append(cp).append(EOL);
return null;
}
@Override
public String visitDN(DNPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_VALID_DN.get());
final DN baseDN = prop.getBaseDN();
if (baseDN != null) {
b.append(": ").append(baseDN);
} else {
b.append(".");
}
b.append(cp).append(EOL);
return null;
}
@Override
public String visitDuration(DurationPropertyDefinition prop, Void p) {
b.append(REF_DSCFG_DURATION_SYNTAX_REL_URL.get()).append(EOL);
b.append(op);
if (prop.isAllowUnlimited()) {
b.append(REF_DSCFG_ALLOW_UNLIMITED.get()).append(" ");
}
if (prop.getMaximumUnit() != null) {
final String maxUnitName = prop.getMaximumUnit().getLongName();
b.append(REF_DSCFG_DURATION_MAX_UNIT.get(maxUnitName)).append(".");
}
final DurationUnit baseUnit = prop.getBaseUnit();
final long lowerLimit = valueOf(baseUnit, prop.getLowerLimit());
final String unitName = baseUnit.getLongName();
b.append(REF_DSCFG_DURATION_LOWER_LIMIT.get(lowerLimit, unitName)).append(".");
if (prop.getUpperLimit() != null) {
final long upperLimit = valueOf(baseUnit, prop.getUpperLimit());
b.append(REF_DSCFG_DURATION_UPPER_LIMIT.get(upperLimit, unitName)).append(".");
}
b.append(cp).append(EOL);
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) {
b.append("").append(EOL);
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);
}
b.append(" ").append(EOL);
return null;
}
@Override
public String visitInteger(IntegerPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_INT.get()).append(". ")
.append(REF_DSCFG_INT_LOWER_LIMIT.get(prop.getLowerLimit())).append(".");
if (prop.getUpperLimit() != null) {
b.append(" ").append(REF_DSCFG_INT_UPPER_LIMIT.get(prop.getUpperLimit())).append(".");
}
if (prop.isAllowUnlimited()) {
b.append(" ").append(REF_DSCFG_ALLOW_UNLIMITED.get());
}
if (prop.getUnitSynopsis() != null) {
b.append(" ").append(REF_DSCFG_INT_UNIT.get(prop.getUnitSynopsis())).append(".");
}
b.append(cp).append(EOL);
return null;
}
@Override
public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_IP_ADDRESS.get()).append(cp).append(EOL);
return null;
}
@Override
public String visitIPAddressMask(IPAddressMaskPropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_IP_ADDRESS_MASK.get()).append(cp).append(EOL);
return null;
}
@Override
public String visitSize(SizePropertyDefinition prop, Void p) {
b.append(op);
if (prop.getLowerLimit() != 0) {
b.append(REF_DSCFG_INT_LOWER_LIMIT.get(prop.getLowerLimit())).append(".");
}
if (prop.getUpperLimit() != null) {
b.append(REF_DSCFG_INT_UPPER_LIMIT.get(prop.getUpperLimit())).append(".");
}
if (prop.isAllowUnlimited()) {
b.append(REF_DSCFG_ALLOW_UNLIMITED.get());
}
b.append(cp).append(EOL);
return null;
}
@Override
public String visitString(StringPropertyDefinition prop, Void p) {
b.append(op);
if (prop.getPatternSynopsis() != null) {
b.append(prop.getPatternSynopsis());
} else {
b.append(REF_DSCFG_STRING.get());
}
b.append(cp).append(EOL);
return null;
}
@Override
public String visitUnknown(PropertyDefinition prop, Void p) {
b.append(op).append(REF_DSCFG_UNKNOWN.get()).append(cp).append(EOL);
return null;
}
};
// Invoke the visitor against the property definition.
prop.accept(visitor, null);
}
private String getLink(String target) {
return " ";
}
}
/** The name of this tool. */
static final String DSCONFIGTOOLNAME = "dsconfig";
/** The name of a command-line script used to launch an administrative tool. */
static final String PROPERTY_SCRIPT_NAME = "org.opends.server.scriptName";
/** A menu call-back which runs a sub-command interactively. */
private class SubCommandHandlerMenuCallback implements MenuCallback {
/** The sub-command handler. */
private final SubCommandHandler handler;
/**
* Creates a new sub-command handler call-back.
*
* @param handler
* The sub-command handler.
*/
public SubCommandHandlerMenuCallback(SubCommandHandler handler) {
this.handler = handler;
}
/** {@inheritDoc} */
@Override
public MenuResult invoke(ConsoleApplication app) throws ClientException {
try {
final MenuResult result = handler.run(app, factory);
if (result.isQuit()) {
return result;
}
if (result.isSuccess() && isInteractive() && handler.isCommandBuilderUseful()) {
printCommandBuilder(getCommandBuilder(handler));
}
// Success or cancel.
app.println();
app.pressReturnToContinue();
return MenuResult.again();
} catch (ArgumentException e) {
app.errPrintln(e.getMessageObject());
return MenuResult.success(1);
} catch (ClientException e) {
app.errPrintln(e.getMessageObject());
return MenuResult.success(e.getReturnCode());
}
}
}
/** The interactive mode sub-menu implementation. */
private class SubMenuCallback implements MenuCallback {
/** The menu. */
private final Menu menu;
/**
* Creates a new sub-menu implementation.
*
* @param app
* The console application.
* @param rd
* The relation definition.
* @param ch
* The optional create sub-command.
* @param dh
* The optional delete sub-command.
* @param lh
* The optional list sub-command.
* @param sh
* The option set-prop sub-command.
*/
public SubMenuCallback(ConsoleApplication app, RelationDefinition, ?> rd, CreateSubCommandHandler, ?> ch,
DeleteSubCommandHandler dh, ListSubCommandHandler lh, SetPropSubCommandHandler sh) {
LocalizableMessage userFriendlyName = rd.getUserFriendlyName();
LocalizableMessage userFriendlyPluralName = null;
if (rd instanceof InstantiableRelationDefinition, ?>) {
InstantiableRelationDefinition, ?> ir = (InstantiableRelationDefinition, ?>) rd;
userFriendlyPluralName = ir.getUserFriendlyPluralName();
} else if (rd instanceof SetRelationDefinition, ?>) {
SetRelationDefinition, ?> sr = (SetRelationDefinition, ?>) rd;
userFriendlyPluralName = sr.getUserFriendlyPluralName();
}
final MenuBuilder builder = new MenuBuilder(app);
builder.setTitle(INFO_DSCFG_HEADING_COMPONENT_MENU_TITLE.get(userFriendlyName));
builder.setPrompt(INFO_DSCFG_HEADING_COMPONENT_MENU_PROMPT.get());
if (lh != null) {
final SubCommandHandlerMenuCallback callback = new SubCommandHandlerMenuCallback(lh);
final Arg1 msg = userFriendlyPluralName != null
? INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_PLURAL
: INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_SINGULAR;
builder.addNumberedOption(msg.get(userFriendlyPluralName), callback);
}
if (ch != null) {
final SubCommandHandlerMenuCallback callback = new SubCommandHandlerMenuCallback(ch);
builder.addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_CREATE.get(userFriendlyName), callback);
}
if (sh != null) {
final SubCommandHandlerMenuCallback callback = new SubCommandHandlerMenuCallback(sh);
final Arg1 msg = userFriendlyPluralName != null
? INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_PLURAL
: INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_SINGULAR;
builder.addNumberedOption(msg.get(userFriendlyName), callback);
}
if (dh != null) {
final SubCommandHandlerMenuCallback callback = new SubCommandHandlerMenuCallback(dh);
builder.addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_DELETE.get(userFriendlyName), callback);
}
builder.addBackOption(true);
builder.addQuitOption();
this.menu = builder.toMenu();
}
/** {@inheritDoc} */
@Override
public final MenuResult invoke(ConsoleApplication app) throws ClientException {
try {
app.println();
app.println();
final MenuResult result = menu.run();
if (result.isCancel()) {
return MenuResult.again();
}
return result;
} catch (ClientException e) {
app.errPrintln(e.getMessageObject());
return MenuResult.success(1);
}
}
}
/**
* The type name which will be used for the most generic managed object types when they are instantiable and
* intended for customization only.
*/
public static final String CUSTOM_TYPE = "custom";
/**
* The type name which will be used for the most generic managed object types when they are instantiable and not
* intended for customization.
*/
public static final String GENERIC_TYPE = "generic";
/**
* Prints the provided error message if the provided application is
* interactive, throws a {@link ClientException} with provided error code
* and message otherwise.
*
* @param app
* The console application where the message should be printed.
* @param msg
* The human readable error message.
* @param errorCode
* The operation error code.
* @return A generic cancel menu result if application is interactive.
* @throws ClientException
* If the application is not interactive.
*/
static MenuResult interactivePrintOrThrowError(ConsoleApplication app,
LocalizableMessage msg, ReturnCode errorCode) throws ClientException {
if (app.isInteractive()) {
app.errPrintln();
app.errPrintln(msg);
return MenuResult.cancel();
} else {
throw new ClientException(errorCode, msg);
}
}
private long sessionStartTime;
private boolean sessionStartTimePrinted;
private int sessionEquivalentOperationNumber;
/**
* Provides the command-line arguments to the main application for processing.
*
* @param args
* The set of command-line arguments provided to this program.
*/
public static void main(String[] args) {
int exitCode = main(args, System.out, System.err);
if (exitCode != ReturnCode.SUCCESS.get()) {
System.exit(filterExitCode(exitCode));
}
}
/**
* Provides the command-line arguments to the main application for processing and returns the exit code as an
* integer.
*
* @param args
* The set of command-line arguments provided to this program.
* @param outStream
* The output stream for standard output.
* @param errStream
* The output stream for standard error.
* @return Zero to indicate that the program completed successfully, or non-zero to indicate that an error occurred.
*/
public static int main(String[] args, OutputStream outStream, OutputStream errStream) {
final DSConfig app = new DSConfig(outStream, errStream);
app.sessionStartTime = System.currentTimeMillis();
if (!ConfigurationFramework.getInstance().isInitialized()) {
try {
ConfigurationFramework.getInstance().initialize();
} catch (ConfigException e) {
app.errPrintln(e.getMessageObject());
return ReturnCode.ERROR_INITIALIZING_SERVER.get();
}
}
// Run the application.
return app.run(args);
}
/** The argument which should be used to request advanced mode. */
private BooleanArgument advancedModeArgument;
/**
* The factory which the application should use to retrieve its management context.
*/
private LDAPManagementContextFactory factory;
/**
* Flag indicating whether or not the global arguments have already been initialized.
*/
private boolean globalArgumentsInitialized;
/** The sub-command handler factory. */
private SubCommandHandlerFactory handlerFactory;
/** Mapping of sub-commands to their implementations. */
private final Map handlers = new HashMap();
/** Indicates whether or not a sub-command was provided. */
private boolean hasSubCommand = true;
/** The argument which should be used to read dsconfig commands from a file. */
private StringArgument batchFileArgument;
/** The argument which should be used to request non interactive behavior. */
private BooleanArgument noPromptArgument;
/**
* The argument that the user must set to display the equivalent non-interactive mode argument.
*/
private BooleanArgument displayEquivalentArgument;
/**
* The argument that allows the user to dump the equivalent non-interactive command to a file.
*/
private StringArgument equivalentCommandFileArgument;
/** The command-line argument parser. */
private final SubCommandArgumentParser parser;
/** The argument which should be used to request quiet output. */
private BooleanArgument quietArgument;
/** The argument which should be used to request script-friendly output. */
private BooleanArgument scriptFriendlyArgument;
/** The argument which should be used to request usage information. */
private BooleanArgument showUsageArgument;
/** The argument which should be used to request verbose output. */
private BooleanArgument verboseArgument;
/** The argument which should be used to indicate the properties file. */
private StringArgument propertiesFileArgument;
/**
* The argument which should be used to indicate that we will not look for properties file.
*/
private BooleanArgument noPropertiesFileArgument;
/**
* Creates a new DSConfig application instance.
*
* @param out
* The application output stream.
* @param err
* The application error stream.
*/
private DSConfig(OutputStream out, OutputStream err) {
super(new PrintStream(out), new PrintStream(err));
this.parser = new SubCommandArgumentParser(getClass().getName(), INFO_DSCFG_TOOL_DESCRIPTION.get(), false);
this.parser.setVersionHandler(new VersionHandler() {
@Override
public void printVersion() {
System.out.println(getVersionString());
}
private String getVersionString() {
try {
final Enumeration resources = getClass().getClassLoader().getResources(
"META-INF/maven/org.forgerock.opendj/opendj-config/pom.properties");
while (resources.hasMoreElements()) {
final Properties props = new Properties();
props.load(resources.nextElement().openStream());
return (String) props.get("version");
}
} catch (IOException e) {
errPrintln(LocalizableMessage.raw(e.getMessage()));
}
return "";
}
});
if (System.getProperty("org.forgerock.opendj.gendoc") != null) {
this.parser.setUsageHandler(new DSConfigSubCommandUsageHandler());
}
}
/** {@inheritDoc} */
@Override
public boolean isAdvancedMode() {
return advancedModeArgument.isPresent();
}
/** {@inheritDoc} */
@Override
public boolean isInteractive() {
return !noPromptArgument.isPresent();
}
/** {@inheritDoc} */
@Override
public boolean isMenuDrivenMode() {
return !hasSubCommand;
}
/** {@inheritDoc} */
@Override
public boolean isQuiet() {
return quietArgument.isPresent();
}
/** {@inheritDoc} */
@Override
public boolean isScriptFriendly() {
return scriptFriendlyArgument.isPresent();
}
/** {@inheritDoc} */
@Override
public boolean isVerbose() {
return verboseArgument.isPresent();
}
/** Displays the provided error message followed by a help usage reference. */
private void displayErrorMessageAndUsageReference(LocalizableMessage message) {
errPrintln(message);
errPrintln();
errPrintln(parser.getHelpUsageReference());
}
/**
* Registers the global arguments with the argument parser.
*
* @throws ArgumentException
* If a global argument could not be registered.
*/
private void initializeGlobalArguments() throws ArgumentException {
if (!globalArgumentsInitialized) {
verboseArgument = CommonArguments.getVerbose();
quietArgument = CommonArguments.getQuiet();
scriptFriendlyArgument = CommonArguments.getScriptFriendly();
noPromptArgument = CommonArguments.getNoPrompt();
advancedModeArgument = CommonArguments.getAdvancedMode();
showUsageArgument = CommonArguments.getShowUsage();
batchFileArgument = new StringArgument(OPTION_LONG_BATCH_FILE_PATH, OPTION_SHORT_BATCH_FILE_PATH,
OPTION_LONG_BATCH_FILE_PATH, false, false, true, INFO_BATCH_FILE_PATH_PLACEHOLDER.get(), null,
null, INFO_DESCRIPTION_BATCH_FILE_PATH.get());
displayEquivalentArgument = new BooleanArgument(OPTION_LONG_DISPLAY_EQUIVALENT, null,
OPTION_LONG_DISPLAY_EQUIVALENT, INFO_DSCFG_DESCRIPTION_DISPLAY_EQUIVALENT.get());
equivalentCommandFileArgument = new StringArgument(OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH, null,
OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH, false, false, true, INFO_PATH_PLACEHOLDER.get(), null,
null, INFO_DSCFG_DESCRIPTION_EQUIVALENT_COMMAND_FILE_PATH.get());
propertiesFileArgument = new StringArgument("propertiesFilePath", null, OPTION_LONG_PROP_FILE_PATH, false,
false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_PROP_FILE_PATH.get());
noPropertiesFileArgument = new BooleanArgument("noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
INFO_DESCRIPTION_NO_PROP_FILE.get());
// Register the global arguments.
ArgumentGroup toolOptionsGroup = new ArgumentGroup(INFO_DSCFG_DESCRIPTION_OPTIONS_ARGS.get(), 2);
parser.addGlobalArgument(advancedModeArgument, toolOptionsGroup);
parser.addGlobalArgument(showUsageArgument);
parser.setUsageArgument(showUsageArgument, getOutputStream());
parser.addGlobalArgument(verboseArgument);
parser.addGlobalArgument(quietArgument);
parser.addGlobalArgument(scriptFriendlyArgument);
parser.addGlobalArgument(noPromptArgument);
parser.addGlobalArgument(batchFileArgument);
parser.addGlobalArgument(displayEquivalentArgument);
parser.addGlobalArgument(equivalentCommandFileArgument);
parser.addGlobalArgument(propertiesFileArgument);
parser.setFilePropertiesArgument(propertiesFileArgument);
parser.addGlobalArgument(noPropertiesFileArgument);
parser.setNoPropertiesFileArgument(noPropertiesFileArgument);
globalArgumentsInitialized = true;
}
}
/**
* Registers the sub-commands with the argument parser. This method uses the administration framework introspection
* APIs to determine the overall structure of the command-line.
*
* @throws ArgumentException
* If a sub-command could not be created.
*/
private void initializeSubCommands() throws ArgumentException {
if (handlerFactory == null) {
handlerFactory = new SubCommandHandlerFactory(parser);
final Comparator c = new Comparator() {
@Override
public int compare(SubCommand o1, SubCommand o2) {
return o1.getName().compareTo(o2.getName());
}
};
Map> groups = new TreeMap>();
SortedSet allSubCommands = new TreeSet(c);
for (SubCommandHandler handler : handlerFactory.getAllSubCommandHandlers()) {
SubCommand sc = handler.getSubCommand();
handlers.put(sc, handler);
allSubCommands.add(sc);
// Add the sub-command to its groups.
for (Tag tag : handler.getTags()) {
SortedSet group = groups.get(tag);
if (group == null) {
group = new TreeSet(c);
groups.put(tag, group);
}
group.add(sc);
}
}
// Register the usage arguments.
for (Map.Entry> group : groups.entrySet()) {
Tag tag = group.getKey();
SortedSet subCommands = group.getValue();
String option = OPTION_LONG_HELP + "-" + tag.getName();
String synopsis = tag.getSynopsis().toString().toLowerCase();
BooleanArgument arg = new BooleanArgument(option, null, option,
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE.get(synopsis));
parser.addGlobalArgument(arg);
parser.setUsageGroupArgument(arg, subCommands);
}
// Register the --help-all argument.
String option = OPTION_LONG_HELP + "-all";
BooleanArgument arg = new BooleanArgument(option, null, option,
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_ALL.get());
parser.addGlobalArgument(arg);
parser.setUsageGroupArgument(arg, allSubCommands);
}
}
/**
* Parses the provided command-line arguments and makes the appropriate changes to the Directory Server
* configuration.
*
* @param args
* The command-line arguments provided to this program.
* @return The exit code from the configuration processing. A nonzero value indicates that there was some kind of
* problem during the configuration processing.
*/
private int run(String[] args) {
// Register global arguments and sub-commands.
try {
initializeGlobalArguments();
initializeSubCommands();
} catch (ArgumentException e) {
errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage()));
return ReturnCode.ERROR_USER_DATA.get();
}
ConnectionFactoryProvider cfp = null;
try {
cfp = new ConnectionFactoryProvider(parser, this, CliConstants.DEFAULT_ROOT_USER_DN,
CliConstants.DEFAULT_ADMINISTRATION_CONNECTOR_PORT, true);
cfp.setIsAnAdminConnection();
// Parse the command-line arguments provided to this program.
parser.parseArguments(args);
checkForConflictingArguments();
} catch (ArgumentException ae) {
displayErrorMessageAndUsageReference(ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
return ReturnCode.CONFLICTING_ARGS.get();
}
// If the usage/version argument was provided, then we don't need
// to do anything else.
if (parser.usageOrVersionDisplayed()) {
return ReturnCode.SUCCESS.get();
}
// Check that we can write on the provided path where we write the
// equivalent non-interactive commands.
if (equivalentCommandFileArgument.isPresent()) {
final String file = equivalentCommandFileArgument.getValue();
if (!canWrite(file)) {
errPrintln(ERR_DSCFG_CANNOT_WRITE_EQUIVALENT_COMMAND_LINE_FILE.get(file));
return ReturnCode.ERROR_UNEXPECTED.get();
} else if (new File(file).isDirectory()) {
errPrintln(ERR_DSCFG_EQUIVALENT_COMMAND_LINE_FILE_DIRECTORY.get(file));
return ReturnCode.ERROR_UNEXPECTED.get();
}
}
// Creates the management context factory which is based on the connection
// provider factory and an authenticated connection factory.
try {
factory = new LDAPManagementContextFactory(cfp);
} catch (ArgumentException e) {
displayErrorMessageAndUsageReference(ERR_ERROR_PARSING_ARGS.get(e.getMessage()));
return ReturnCode.CONFLICTING_ARGS.get();
}
// Handle batch file if any
if (batchFileArgument.isPresent()) {
handleBatchFile(args);
return ReturnCode.SUCCESS.get();
}
int retCode = 0;
hasSubCommand = parser.getSubCommand() != null;
if (!hasSubCommand) {
if (isInteractive()) {
// Top-level interactive mode.
retCode = runInteractiveMode();
} else {
displayErrorMessageAndUsageReference(
ERR_ERROR_PARSING_ARGS.get(ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get()));
retCode = ReturnCode.ERROR_USER_DATA.get();
}
} else {
// Retrieve the sub-command implementation and run it.
retCode = runSubCommand(handlers.get(parser.getSubCommand()));
}
factory.close();
return retCode;
}
private void checkForConflictingArguments() throws ArgumentException {
throwIfConflictingArgsSet(quietArgument, verboseArgument);
throwIfSetInInteractiveMode(batchFileArgument);
throwIfSetInInteractiveMode(quietArgument);
throwIfConflictingArgsSet(scriptFriendlyArgument, verboseArgument);
throwIfConflictingArgsSet(noPropertiesFileArgument, propertiesFileArgument);
}
private void throwIfSetInInteractiveMode(Argument arg) throws ArgumentException {
if (arg.isPresent() && !noPromptArgument.isPresent()) {
throw new ArgumentException(ERR_DSCFG_ERROR_QUIET_AND_INTERACTIVE_INCOMPATIBLE.get(
arg.getLongIdentifier(), noPromptArgument.getLongIdentifier()));
}
}
private void throwIfConflictingArgsSet(Argument arg1, Argument arg2) throws ArgumentException {
if (arg1.isPresent() && arg2.isPresent()) {
throw new ArgumentException(ERR_TOOL_CONFLICTING_ARGS.get(
arg1.getLongIdentifier(), arg2.getLongIdentifier()));
}
}
/** Run the top-level interactive console. */
private int runInteractiveMode() {
ConsoleApplication app = this;
// Build menu structure.
final Comparator> c = new Comparator>() {
@Override
public int compare(RelationDefinition, ?> rd1, RelationDefinition, ?> rd2) {
final String s1 = rd1.getUserFriendlyName().toString();
final String s2 = rd2.getUserFriendlyName().toString();
return s1.compareToIgnoreCase(s2);
}
};
final Set> relations = new TreeSet>(c);
final Map, CreateSubCommandHandler, ?>> createHandlers
= new HashMap, CreateSubCommandHandler, ?>>();
final Map, DeleteSubCommandHandler> deleteHandlers
= new HashMap, DeleteSubCommandHandler>();
final Map, ListSubCommandHandler> listHandlers
= new HashMap, ListSubCommandHandler>();
final Map, GetPropSubCommandHandler> getPropHandlers
= new HashMap, GetPropSubCommandHandler>();
final Map, SetPropSubCommandHandler> setPropHandlers
= new HashMap, SetPropSubCommandHandler>();
for (final CreateSubCommandHandler, ?> ch : handlerFactory.getCreateSubCommandHandlers()) {
relations.add(ch.getRelationDefinition());
createHandlers.put(ch.getRelationDefinition(), ch);
}
for (final DeleteSubCommandHandler dh : handlerFactory.getDeleteSubCommandHandlers()) {
relations.add(dh.getRelationDefinition());
deleteHandlers.put(dh.getRelationDefinition(), dh);
}
for (final ListSubCommandHandler lh : handlerFactory.getListSubCommandHandlers()) {
relations.add(lh.getRelationDefinition());
listHandlers.put(lh.getRelationDefinition(), lh);
}
for (final GetPropSubCommandHandler gh : handlerFactory.getGetPropSubCommandHandlers()) {
relations.add(gh.getRelationDefinition());
getPropHandlers.put(gh.getRelationDefinition(), gh);
}
for (final SetPropSubCommandHandler sh : handlerFactory.getSetPropSubCommandHandlers()) {
relations.add(sh.getRelationDefinition());
setPropHandlers.put(sh.getRelationDefinition(), sh);
}
// Main menu.
final MenuBuilder builder = new MenuBuilder(app);
builder.setTitle(INFO_DSCFG_HEADING_MAIN_MENU_TITLE.get());
builder.setPrompt(INFO_DSCFG_HEADING_MAIN_MENU_PROMPT.get());
builder.setMultipleColumnThreshold(0);
for (final RelationDefinition, ?> rd : relations) {
final MenuCallback callback = new SubMenuCallback(app, rd, createHandlers.get(rd),
deleteHandlers.get(rd), listHandlers.get(rd), setPropHandlers.get(rd));
builder.addNumberedOption(rd.getUserFriendlyName(), callback);
}
builder.addQuitOption();
final Menu menu = builder.toMenu();
try {
// Force retrieval of management context.
factory.getManagementContext(app);
} catch (ArgumentException e) {
app.errPrintln(e.getMessageObject());
return ReturnCode.ERROR_UNEXPECTED.get();
} catch (ClientException e) {
app.errPrintln(e.getMessageObject());
return ReturnCode.ERROR_UNEXPECTED.get();
}
try {
app.println();
app.println();
final MenuResult result = menu.run();
if (result.isQuit()) {
return ReturnCode.SUCCESS.get();
} else {
return result.getValue();
}
} catch (ClientException e) {
app.errPrintln(e.getMessageObject());
return ReturnCode.ERROR_UNEXPECTED.get();
}
}
/** Run the provided sub-command handler. */
private int runSubCommand(SubCommandHandler handler) {
try {
final MenuResult result = handler.run(this, factory);
if (result.isSuccess()) {
if (isInteractive() && handler.isCommandBuilderUseful()) {
printCommandBuilder(getCommandBuilder(handler));
}
return result.getValue();
} else {
// User must have quit.
return ReturnCode.ERROR_UNEXPECTED.get();
}
} catch (ArgumentException e) {
errPrintln(e.getMessageObject());
return ReturnCode.ERROR_UNEXPECTED.get();
} catch (ClientException e) {
Throwable cause = e.getCause();
errPrintln();
if (cause instanceof ManagedObjectDecodingException) {
displayManagedObjectDecodingException(this, (ManagedObjectDecodingException) cause);
} else if (cause instanceof MissingMandatoryPropertiesException) {
displayMissingMandatoryPropertyException(this, (MissingMandatoryPropertiesException) cause);
} else if (cause instanceof OperationRejectedException) {
displayOperationRejectedException(this, (OperationRejectedException) cause);
} else {
// Just display the default message.
errPrintln(e.getMessageObject());
}
errPrintln();
return ReturnCode.ERROR_UNEXPECTED.get();
} catch (Exception e) {
errPrintln(LocalizableMessage.raw(stackTraceToSingleLineString(e, true)));
return ReturnCode.ERROR_UNEXPECTED.get();
}
}
/**
* Updates the command builder with the global options: script friendly, verbose, etc. for a given sub command. It
* also adds systematically the no-prompt option.
*
* @param
* SubCommand type.
* @param subCommand
* The sub command handler or common.
* @return The builded command.
*/
CommandBuilder getCommandBuilder(final T subCommand) {
final String commandName = getScriptName();
final String subCommandName;
if (subCommand instanceof SubCommandHandler) {
subCommandName = ((SubCommandHandler) subCommand).getSubCommand().getName();
} else {
subCommandName = (String) subCommand;
}
final CommandBuilder commandBuilder = new CommandBuilder(commandName, subCommandName);
if (factory != null && factory.getContextCommandBuilder() != null) {
commandBuilder.append(factory.getContextCommandBuilder());
}
if (verboseArgument.isPresent()) {
commandBuilder.addArgument(verboseArgument);
}
if (scriptFriendlyArgument.isPresent()) {
commandBuilder.addArgument(scriptFriendlyArgument);
}
commandBuilder.addArgument(noPromptArgument);
if (propertiesFileArgument.isPresent()) {
commandBuilder.addArgument(propertiesFileArgument);
}
if (noPropertiesFileArgument.isPresent()) {
commandBuilder.addArgument(noPropertiesFileArgument);
}
return commandBuilder;
}
private String getScriptName() {
final String commandName = System.getProperty(PROPERTY_SCRIPT_NAME);
if (commandName != null && commandName.length() != 0) {
return commandName;
}
return DSCONFIGTOOLNAME;
}
/**
* Prints the contents of a command builder. This method has been created since SetPropSubCommandHandler calls it.
* All the logic of DSConfig is on this method. It writes the content of the CommandBuilder to the standard output,
* or to a file depending on the options provided by the user.
*
* @param commandBuilder
* the command builder to be printed.
*/
void printCommandBuilder(CommandBuilder commandBuilder) {
if (displayEquivalentArgument.isPresent()) {
println();
// We assume that the app we are running is this one.
println(INFO_DSCFG_NON_INTERACTIVE.get(commandBuilder));
}
if (equivalentCommandFileArgument.isPresent()) {
String file = equivalentCommandFileArgument.getValue();
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(file, true));
if (!sessionStartTimePrinted) {
writer.write(SHELL_COMMENT_SEPARATOR + getSessionStartTimeMessage());
writer.newLine();
sessionStartTimePrinted = true;
}
sessionEquivalentOperationNumber++;
writer.newLine();
writer.write(SHELL_COMMENT_SEPARATOR
+ INFO_DSCFG_EQUIVALENT_COMMAND_LINE_SESSION_OPERATION_NUMBER
.get(sessionEquivalentOperationNumber));
writer.newLine();
writer.write(SHELL_COMMENT_SEPARATOR + getCurrentOperationDateMessage());
writer.newLine();
writer.write(commandBuilder.toString());
writer.newLine();
writer.newLine();
writer.flush();
} catch (IOException ioe) {
errPrintln(ERR_DSCFG_ERROR_WRITING_EQUIVALENT_COMMAND_LINE.get(file, ioe));
} finally {
closeSilently(writer);
}
}
}
/**
* Returns the message to be displayed in the file with the equivalent command-line with information about when the
* session started.
*
* @return the message to be displayed in the file with the equivalent command-line with information about when the
* session started.
*/
private String getSessionStartTimeMessage() {
final String date = formatDateTimeStringForEquivalentCommand(new Date(sessionStartTime));
return INFO_DSCFG_SESSION_START_TIME_MESSAGE.get(getScriptName(), date).toString();
}
private void handleBatchFile(String[] args) {
BufferedReader bReader = null;
try {
final String batchFilePath = batchFileArgument.getValue().trim();
bReader = new BufferedReader(new FileReader(batchFilePath));
List initialArgs = removeBatchArgs(args);
// Split the CLI string into arguments array
String command = "";
String line;
while ((line = bReader.readLine()) != null) {
if ("".equals(line) || line.startsWith("#")) {
// Empty line or comment
continue;
}
// command split in several line support
if (line.endsWith("\\")) {
// command is split into several lines
command += line.substring(0, line.length() - 1);
continue;
} else {
command += line;
}
command = command.trim();
// string between quotes support
command = replaceSpacesInQuotes(command);
// "\ " support
command = command.replace("\\ ", "##");
String displayCommand = command.replace("\\ ", " ");
errPrintln(LocalizableMessage.raw(displayCommand));
// Append initial arguments to the file line
final String[] allArgsArray = buildCommandArgs(initialArgs, command);
int exitCode = main(allArgsArray, getOutputStream(), getErrorStream());
if (exitCode != ReturnCode.SUCCESS.get()) {
System.exit(filterExitCode(exitCode));
}
errPrintln();
// reset command
command = "";
}
} catch (IOException ex) {
errPrintln(ERR_DSCFG_ERROR_READING_BATCH_FILE.get(ex));
} finally {
closeSilently(bReader);
}
}
private String[] buildCommandArgs(List initialArgs, String batchCommand) {
final String[] commandArgs = toCommandArgs(batchCommand);
final int length = commandArgs.length + initialArgs.size();
final List allArguments = new ArrayList(length);
Collections.addAll(allArguments, commandArgs);
allArguments.addAll(initialArgs);
return allArguments.toArray(new String[length]);
}
private String[] toCommandArgs(String command) {
String[] fileArguments = command.split("\\s+");
for (int ii = 0; ii < fileArguments.length; ii++) {
fileArguments[ii] = fileArguments[ii].replace("##", " ");
}
return fileArguments;
}
private List removeBatchArgs(String[] args) {
// Build a list of initial arguments,
// removing the batch file option + its value
final List initialArgs = new ArrayList();
Collections.addAll(initialArgs, args);
for (Iterator it = initialArgs.iterator(); it.hasNext();) {
final String elem = it.next();
if (elem.startsWith("-" + batchFileArgument.getShortIdentifier())
|| elem.contains(batchFileArgument.getLongIdentifier())) {
// Remove both the batch file arg and its value
it.remove();
it.next();
it.remove();
break;
}
}
return initialArgs;
}
/** Replace spaces in quotes by "\ ". */
private String replaceSpacesInQuotes(final String line) {
StringBuilder newLine = new StringBuilder();
boolean inQuotes = false;
for (int ii = 0; ii < line.length(); ii++) {
char ch = line.charAt(ii);
if (ch == '\"' || ch == '\'') {
inQuotes = !inQuotes;
continue;
}
if (inQuotes && ch == ' ') {
newLine.append("\\ ");
} else {
newLine.append(ch);
}
}
return newLine.toString();
}
}