Checkpoint for OPENDJ-1343 Migrate dsconfig
- Moved dsconfig package to opendj-config.
-> Several checkstyle errors still exist and will be fixed after this commit in order to let the changes on main files more readable.
-> BuildVersion.java will be renamed, moved later.
** DsConfig.java
- Added logger.
- Removed checkVersionMismatch and moved it to LDAPManagementContextFactory where the connection is opened.
** BuildVersion.java
- Added function to get the current configuration entry value of "cn=Version,cn=monitor"
** LDAPManagementContextFactory.java
- Added checkVersionMismatch.
**config.properties
- Added messages linked to BuildVersion.
** SDK *check* style modifications on :
- PropertyEditorModification.java
- PropertyValueEditor.java
- HelpSubCommandHandler.java
- SetPropSubCommandHandler.java
- SubCommandHandler.java
- ListSubCommandHandler.java
- CreateSubCommandHandler.java
- PropertyValuePrinter.java
- DeleteSubCommandHandler.java
- SubCommandHandlerFactory.java
- ArgumentExceptionFactory.java
- GetPropSubCommandHandler.java
- CLIProfile.java
** pom file : added dependency on opendj-cli.
16 files added
3 files modified
| | |
| | | <artifactId>opendj-core</artifactId> |
| | | <version>${project.version}</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>org.forgerock.opendj</groupId> |
| | | <artifactId>opendj-cli</artifactId> |
| | | <version>${project.version}</version> |
| | | </dependency> |
| | | </dependencies> |
| | | <properties> |
| | | <opendj.osgi.import> |
| | |
| | | throw new IllegalStateException("configuration framework already initialized."); |
| | | } |
| | | this.installPath = installPath == null ? System.getProperty("user.dir") : installPath; |
| | | this.instancePath = instancePath == null ? installPath : instancePath; |
| | | this.instancePath = instancePath == null ? this.installPath : instancePath; |
| | | this.parent = parent; |
| | | initialize0(); |
| | | return this; |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the installation path. |
| | | * |
| | | * @return The installation path of this instance. |
| | | */ |
| | | public String getInstallPath() { |
| | | return installPath; |
| | | } |
| | | |
| | | /** |
| | | * Returns the instance path. |
| | | * |
| | | * @return The instance path. |
| | | */ |
| | | public String getInstancePath() { |
| | | return instancePath; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; |
| | | import org.forgerock.opendj.config.PropertyException; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.client.IllegalManagedObjectNameException; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException; |
| | | import org.forgerock.opendj.config.client.OperationRejectedException; |
| | | |
| | | import com.forgerock.opendj.cli.Argument; |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.ReturnCode; |
| | | import com.forgerock.opendj.cli.TableBuilder; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * A utility class for converting various admin exception types into argument exceptions. |
| | | */ |
| | | public final class ArgumentExceptionFactory { |
| | | |
| | | /** |
| | | * Creates a ClientException exception from an illegal managed object name exception. |
| | | * |
| | | * @param e |
| | | * The illegal managed object name exception. |
| | | * @param d |
| | | * The managed object definition. |
| | | * @return Returns a ClientException exception. |
| | | */ |
| | | public static ClientException adaptIllegalManagedObjectNameException(IllegalManagedObjectNameException e, |
| | | AbstractManagedObjectDefinition<?, ?> d) { |
| | | String illegalName = e.getIllegalName(); |
| | | PropertyDefinition<?> pd = e.getNamingPropertyDefinition(); |
| | | |
| | | if (illegalName.length() == 0) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_EMPTY.get(d.getUserFriendlyPluralName()); |
| | | return new ClientException(ReturnCode.ERROR_USER_DATA, message); |
| | | } else if (illegalName.trim().length() == 0) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_BLANK.get(d.getUserFriendlyPluralName()); |
| | | return new ClientException(ReturnCode.ERROR_USER_DATA, message); |
| | | } else if (pd != null) { |
| | | try { |
| | | pd.decodeValue(illegalName); |
| | | } catch (PropertyException e1) { |
| | | PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true); |
| | | LocalizableMessage syntax = b.getUsage(pd); |
| | | |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_SYNTAX.get(illegalName, |
| | | d.getUserFriendlyName(), syntax); |
| | | return new ClientException(ReturnCode.ERROR_USER_DATA, message); |
| | | } |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN.get(illegalName, d.getUserFriendlyName()); |
| | | return new ClientException(ReturnCode.ERROR_USER_DATA, message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception from a property exception. |
| | | * |
| | | * @param e |
| | | * The property exception. |
| | | * @param d |
| | | * The managed object definition. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException adaptPropertyException(PropertyException e, |
| | | AbstractManagedObjectDefinition<?, ?> d) { |
| | | return new ArgumentException(e.getMessageObject()); |
| | | } |
| | | |
| | | /** |
| | | * Displays a table listing reasons why a managed object could not be decoded successfully. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param e |
| | | * The managed object decoding exception. |
| | | */ |
| | | public static void displayManagedObjectDecodingException(ConsoleApplication app, ManagedObjectDecodingException e) { |
| | | AbstractManagedObjectDefinition<?, ?> d = e.getPartialManagedObject().getManagedObjectDefinition(); |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | LocalizableMessage msg; |
| | | if (e.getCauses().size() == 1) { |
| | | msg = ERR_GET_HEADING_MODE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_GET_HEADING_MODE_PLURAL.get(ufn); |
| | | } |
| | | |
| | | app.println(msg); |
| | | app.println(); |
| | | TableBuilder builder = new TableBuilder(); |
| | | for (PropertyException pe : e.getCauses()) { |
| | | ArgumentException ae = adaptPropertyException(pe, d); |
| | | builder.startRow(); |
| | | builder.appendCell("*"); |
| | | builder.appendCell(ae.getMessage()); |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setColumnWidth(1, 0); |
| | | printer.setIndentWidth(4); |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** |
| | | * Displays a table listing missing mandatory properties. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param e |
| | | * The missing mandatory property exception. |
| | | */ |
| | | public static void displayMissingMandatoryPropertyException(ConsoleApplication app, |
| | | MissingMandatoryPropertiesException e) { |
| | | LocalizableMessage ufn = e.getUserFriendlyName(); |
| | | LocalizableMessage msg; |
| | | if (e.isCreate()) { |
| | | if (e.getCauses().size() == 1) { |
| | | msg = ERR_CREATE_HEADING_MMPE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_CREATE_HEADING_MMPE_PLURAL.get(ufn); |
| | | } |
| | | } else { |
| | | if (e.getCauses().size() == 1) { |
| | | msg = ERR_MODIFY_HEADING_MMPE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_MODIFY_HEADING_MMPE_PLURAL.get(ufn); |
| | | } |
| | | } |
| | | |
| | | app.println(msg); |
| | | app.println(); |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.addSortKey(0); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get()); |
| | | |
| | | PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true); |
| | | for (PropertyException pe : e.getCauses()) { |
| | | PropertyDefinition<?> pd = pe.getPropertyDefinition(); |
| | | builder.startRow(); |
| | | builder.appendCell(pd.getName()); |
| | | builder.appendCell(b.getUsage(pd)); |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(true); |
| | | printer.setColumnWidth(1, 0); |
| | | printer.setIndentWidth(4); |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** |
| | | * Displays a table listing the reasons why an operation was rejected. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param e |
| | | * The operation rejected exception. |
| | | */ |
| | | public static void displayOperationRejectedException(ConsoleApplication app, OperationRejectedException e) { |
| | | LocalizableMessage ufn = e.getUserFriendlyName(); |
| | | LocalizableMessage msg; |
| | | switch (e.getOperationType()) { |
| | | case CREATE: |
| | | if (e.getMessages().size() == 1) { |
| | | msg = ERR_DSCFG_ERROR_CREATE_ORE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_DSCFG_ERROR_CREATE_ORE_PLURAL.get(ufn); |
| | | } |
| | | break; |
| | | case DELETE: |
| | | if (e.getMessages().size() == 1) { |
| | | msg = ERR_DSCFG_ERROR_DELETE_ORE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_DSCFG_ERROR_DELETE_ORE_PLURAL.get(ufn); |
| | | } |
| | | break; |
| | | default: |
| | | if (e.getMessages().size() == 1) { |
| | | msg = ERR_DSCFG_ERROR_MODIFY_ORE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_DSCFG_ERROR_MODIFY_ORE_PLURAL.get(ufn); |
| | | } |
| | | break; |
| | | } |
| | | |
| | | app.println(msg); |
| | | app.println(); |
| | | TableBuilder builder = new TableBuilder(); |
| | | for (LocalizableMessage reason : e.getMessages()) { |
| | | builder.startRow(); |
| | | builder.appendCell("*"); |
| | | builder.appendCell(reason); |
| | | } |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setColumnWidth(1, 0); |
| | | printer.setIndentWidth(4); |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property modification argument is incompatible with a |
| | | * previous modification argument. |
| | | * |
| | | * @param arg |
| | | * The incompatible argument. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException incompatiblePropertyModification(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_INCOMPATIBLE_PROPERTY_MOD.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when the client has not specified a bind password. |
| | | * |
| | | * @param bindDN |
| | | * The name of the user requiring a password. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingBindPassword(String bindDN) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_PASSWORD.get(bindDN); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when the client has not specified a bind password. |
| | | * |
| | | * @param bindDN |
| | | * The name of the user requiring a password. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingBindPassword(char[] bindDN) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_PASSWORD.get(bindDN); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when an argument, which is mandatory when the application is |
| | | * non-interactive, has not been specified. |
| | | * |
| | | * @param arg |
| | | * The missing argument. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingMandatoryNonInteractiveArgument(Argument arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MISSING_NON_INTERACTIVE_ARG.get(arg.getLongIdentifier()); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property value argument is invalid because it does not |
| | | * a property name. |
| | | * |
| | | * @param arg |
| | | * The argument having the missing property name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingNameInPropertyArgument(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_VALUE.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property modification argument is invalid because it |
| | | * does not a property name. |
| | | * |
| | | * @param arg |
| | | * The argument having the missing property name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingNameInPropertyModification(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property value argument is invalid because it does not |
| | | * contain a separator between the property name and its value. |
| | | * |
| | | * @param arg |
| | | * The argument having a missing separator. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingSeparatorInPropertyArgument(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_VALUE.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property modification argument is invalid because it |
| | | * does not contain a separator between the property name and its value. |
| | | * |
| | | * @param arg |
| | | * The argument having a missing separator. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingSeparatorInPropertyModification(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_MOD.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property value argument is invalid because it does not |
| | | * a property value. |
| | | * |
| | | * @param arg |
| | | * The argument having the missing property value. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingValueInPropertyArgument(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_VALUE.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property modification argument is invalid because it |
| | | * does not a property value. |
| | | * |
| | | * @param arg |
| | | * The argument having the missing property value. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException missingValueInPropertyModification(String arg) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD.get(arg); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when the connection parameters could not be read from the |
| | | * standard input. |
| | | * |
| | | * @param cause |
| | | * The reason why the connection parameters could not be read. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unableToReadConnectionParameters(Exception cause) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(cause.getMessage()); |
| | | return new ArgumentException(message, cause); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when the bind password could not be read from the standard |
| | | * input because the application is non-interactive. |
| | | * |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unableToReadBindPasswordInteractively() { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_BIND_PASSWORD_NONINTERACTIVE.get(); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when an attempt is made to reset a mandatory property that |
| | | * does not have any default values. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @param name |
| | | * The name of the mandatory property. |
| | | * @param setOption |
| | | * The name of the option which should be used to set the property's values. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unableToResetMandatoryProperty(AbstractManagedObjectDefinition<?, ?> d, |
| | | String name, String setOption) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_RESET_MANDATORY_PROPERTY.get( |
| | | d.getUserFriendlyPluralName(), name, setOption); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when an attempt is made to reset a property with a value. |
| | | * |
| | | * @param name |
| | | * The name of the mandatory property. |
| | | * @param resetOption |
| | | * The name of the option which should be used to reset the property's values. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unableToResetPropertyWithValue(String name, String resetOption) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_RESET_PROPERTY_WITH_VALUE.get(resetOption, name, |
| | | resetOption); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when an attempt is made to set the naming property for a |
| | | * managed object during creation. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @param pd |
| | | * The naming property definition. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unableToSetNamingProperty(AbstractManagedObjectDefinition<?, ?> d, |
| | | PropertyDefinition<?> pd) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_SET_NAMING_PROPERTY.get(pd.getName(), |
| | | d.getUserFriendlyName()); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a component category argument is not recognized. |
| | | * |
| | | * @param categoryName |
| | | * The unrecognized component category. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownCategory(String categoryName) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CATEGORY_UNRECOGNIZED.get(categoryName); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property name is not recognized. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @param name |
| | | * The unrecognized property name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownProperty(AbstractManagedObjectDefinition<?, ?> d, String name) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED.get(name, d.getUserFriendlyPluralName()); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a property name is not recognized. |
| | | * |
| | | * @param name |
| | | * The unrecognized property name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownProperty(String name) { |
| | | LocalizableMessage message = ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN.get(name); |
| | | return new ArgumentException(message); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a sub-type argument in a create-xxx sub-command is not |
| | | * recognized. |
| | | * |
| | | * @param r |
| | | * The relation definition. |
| | | * @param typeName |
| | | * The unrecognized property sub-type. |
| | | * @param typeUsage |
| | | * A usage string describing the allowed sub-types. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownSubType(RelationDefinition<?, ?> r, String typeName, String typeUsage) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED |
| | | .get(typeName, r.getUserFriendlyName(), typeUsage); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a managed object type argument is not associated with a |
| | | * category. |
| | | * |
| | | * @param categoryName |
| | | * The component category. |
| | | * @param typeName |
| | | * The unrecognized component type. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownTypeForCategory(String typeName, String categoryName) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED.get(typeName, categoryName); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a multi-valued property does not contain a given value. |
| | | * |
| | | * @param value |
| | | * The property value. |
| | | * @param propertyName |
| | | * The property name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownValueForMultiValuedProperty(String value, String propertyName) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_VALUE_DOES_NOT_EXIST.get(value, propertyName); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument exception which should be used when a child component does not exist. |
| | | * |
| | | * @param componentName |
| | | * The component name. |
| | | * @return Returns an argument exception. |
| | | */ |
| | | public static ArgumentException unknownValueForChildComponent(String componentName) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(componentName); |
| | | return new ArgumentException(msg); |
| | | } |
| | | |
| | | /** |
| | | * Creates a CLI exception which should be used when a managed object is retrieved but does not have the correct |
| | | * type appropriate for the associated sub-command. |
| | | * |
| | | * @param r |
| | | * The relation definition. |
| | | * @param d |
| | | * The definition of the managed object that was retrieved. |
| | | * @param subcommandName |
| | | * the sub-command name. |
| | | * @return Returns a Client exception. |
| | | */ |
| | | public static ClientException wrongManagedObjectType(RelationDefinition<?, ?> r, ManagedObjectDefinition<?, ?> d, |
| | | String subcommandName) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED_FOR_SUBCOMMAND.get(d.getUserFriendlyName(), |
| | | subcommandName); |
| | | return new ClientException(ReturnCode.ERROR_USER_DATA, msg); |
| | | } |
| | | |
| | | // Prevent instantiation. |
| | | private ArgumentExceptionFactory() { |
| | | // No implementation required. |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2013-2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.ldap.ConfigMessages.ERR_BUILDVERSION_MISMATCH; |
| | | import static com.forgerock.opendj.ldap.ConfigMessages.ERR_BUILDVERSION_MALFORMED; |
| | | import static com.forgerock.opendj.ldap.ConfigMessages.ERR_BUILDVERSION_NOT_FOUND; |
| | | import static com.forgerock.opendj.ldap.ConfigMessages.ERR_CONFIGVERSION_NOT_FOUND; |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.File; |
| | | import java.io.FileReader; |
| | | import java.io.IOException; |
| | | import java.util.Arrays; |
| | | |
| | | import org.forgerock.opendj.config.ConfigurationFramework; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | |
| | | /** |
| | | * Represents a particular version of OpenDJ useful for making comparisons between versions. FIXME TODO Move this file |
| | | * in ? package. |
| | | */ |
| | | public class BuildVersion implements Comparable<BuildVersion> { |
| | | |
| | | private final int major; |
| | | private final int minor; |
| | | private final int point; |
| | | private final long rev; |
| | | |
| | | /** |
| | | * Creates a new build version using the provided version information. |
| | | * |
| | | * @param major |
| | | * Major release version number. |
| | | * @param minor |
| | | * Minor release version number. |
| | | * @param point |
| | | * Point release version number. |
| | | * @param rev |
| | | * VCS revision number. |
| | | */ |
| | | public BuildVersion(final int major, final int minor, final int point, final long rev) { |
| | | this.major = major; |
| | | this.minor = minor; |
| | | this.point = point; |
| | | this.rev = rev; |
| | | } |
| | | |
| | | /** |
| | | * Returns the build version as specified in the entry "cn=Version,cn=monitor". |
| | | * |
| | | * @param connection |
| | | * The connection to use to read the entry. |
| | | * @return The build version as specified in the current installation configuration. |
| | | * @throws ConfigException |
| | | * Sends an exception if it is impossible to retrieve the version configuration entry. |
| | | */ |
| | | public static BuildVersion binaryVersion(final Connection connection) throws ConfigException { |
| | | try { |
| | | final SearchResultEntry entry = connection.readEntry("cn=Version,cn=monitor", "majorVersion", |
| | | "minorVersion", "pointVersion", "revisionNumber"); |
| | | final int eMajor = Integer.valueOf(entry.getAttribute("majorVersion").firstValueAsString()); |
| | | final int eMinor = Integer.valueOf(entry.getAttribute("minorVersion").firstValueAsString()); |
| | | final int ePoint = Integer.valueOf(entry.getAttribute("pointVersion").firstValueAsString()); |
| | | final long eRev = Long.valueOf(entry.getAttribute("revisionNumber").firstValueAsString()); |
| | | return new BuildVersion(eMajor, eMinor, ePoint, eRev); |
| | | } catch (ErrorResultException e) { |
| | | throw new ConfigException(ERR_CONFIGVERSION_NOT_FOUND.get()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Checks if the binary version is the same than the instance version. If not, a configuration exception is thrown. |
| | | * |
| | | * @param connection |
| | | * The connection to use to read the configuration entry. |
| | | * @throws ConfigException |
| | | * Sends an exception if the version mismatch. |
| | | */ |
| | | public static void checkVersionMismatch(final Connection connection) throws ConfigException { |
| | | final BuildVersion binaryVersion = BuildVersion.binaryVersion(connection); |
| | | final BuildVersion instanceVersion = BuildVersion.instanceVersion(); |
| | | if (!binaryVersion.toString().equals(instanceVersion.toString())) { |
| | | throw new ConfigException(ERR_BUILDVERSION_MISMATCH.get(binaryVersion, instanceVersion)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Reads the instance version from config/buildinfo. |
| | | * |
| | | * @return The instance version from config/buildinfo. |
| | | * @throws ConfigException |
| | | * If an error occurred while reading or parsing the version. |
| | | */ |
| | | public static BuildVersion instanceVersion() throws ConfigException { |
| | | final String buildInfo = ConfigurationFramework.getInstance().getInstancePath() + File.separator + "config" |
| | | + File.separator + "buildinfo"; |
| | | BufferedReader reader = null; |
| | | try { |
| | | reader = new BufferedReader(new FileReader(buildInfo)); |
| | | final String s = reader.readLine(); |
| | | if (s != null) { |
| | | return valueOf(s); |
| | | } else { |
| | | throw new ConfigException(ERR_BUILDVERSION_MALFORMED.get(buildInfo)); |
| | | } |
| | | } catch (IOException e) { |
| | | throw new ConfigException(ERR_BUILDVERSION_NOT_FOUND.get(buildInfo)); |
| | | } catch (final IllegalArgumentException e) { |
| | | throw new ConfigException(ERR_BUILDVERSION_MALFORMED.get(buildInfo)); |
| | | } finally { |
| | | closeSilently(reader); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Parses the string argument as a build version. The string must be of the form: |
| | | * |
| | | * <pre> |
| | | * major.minor.point.rev |
| | | * </pre> |
| | | * |
| | | * @param s |
| | | * The string to be parsed as a build version. |
| | | * @return The parsed build version. |
| | | * @throws IllegalArgumentException |
| | | * If the string does not contain a parsable build version. |
| | | */ |
| | | public static BuildVersion valueOf(final String s) { |
| | | final String[] fields = s.split("\\."); |
| | | if (fields.length != 4) { |
| | | throw new IllegalArgumentException("Invalid version string " + s); |
| | | } |
| | | final int major = Integer.parseInt(fields[0]); |
| | | final int minor = Integer.parseInt(fields[1]); |
| | | final int point = Integer.parseInt(fields[2]); |
| | | final long rev = Long.parseLong(fields[3]); |
| | | return new BuildVersion(major, minor, point, rev); |
| | | } |
| | | |
| | | /** |
| | | * Returns the major release version number. |
| | | * |
| | | * @return The major release version number. |
| | | */ |
| | | public int getMajorVersion() { |
| | | return major; |
| | | } |
| | | |
| | | /** |
| | | * Returns the minor release version number. |
| | | * |
| | | * @return The minor release version number. |
| | | */ |
| | | public int getMinorVersion() { |
| | | return minor; |
| | | } |
| | | |
| | | /** |
| | | * Returns the point release version number. |
| | | * |
| | | * @return The point release version number. |
| | | */ |
| | | public int getPointVersion() { |
| | | return point; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public boolean equals(final Object obj) { |
| | | if (this == obj) { |
| | | return true; |
| | | } else if (obj instanceof BuildVersion) { |
| | | final BuildVersion other = (BuildVersion) obj; |
| | | return major == other.major && minor == other.minor && point == other.point && rev == other.rev; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public int compareTo(final BuildVersion version) { |
| | | if (major == version.major) { |
| | | if (minor == version.minor) { |
| | | if (point == version.point) { |
| | | if (rev == version.rev) { |
| | | return 0; |
| | | } else if (rev < version.rev) { |
| | | return -1; |
| | | } |
| | | } else if (point < version.point) { |
| | | return -1; |
| | | } |
| | | } else if (minor < version.minor) { |
| | | return -1; |
| | | } |
| | | } else if (major < version.major) { |
| | | return -1; |
| | | } |
| | | return 1; |
| | | } |
| | | |
| | | /** |
| | | * Returns the VCS revision number. |
| | | * |
| | | * @return The VCS revision number. |
| | | */ |
| | | public long getRevisionNumber() { |
| | | return rev; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public int hashCode() { |
| | | return Arrays.hashCode(new int[] { major, minor, point, (int) (rev >>> 32), (int) (rev & 0xFFFFL) }); |
| | | } |
| | | |
| | | /** |
| | | * Returns the string representation of the version. E.g: |
| | | * |
| | | * <pre> |
| | | * version : 2.8.0.1022 |
| | | * </pre> |
| | | * |
| | | * @return The string representation of the version. |
| | | */ |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append(major); |
| | | builder.append('.'); |
| | | builder.append(minor); |
| | | builder.append('.'); |
| | | builder.append(point); |
| | | builder.append('.'); |
| | | builder.append(rev); |
| | | return builder.toString(); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinitionResource; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | |
| | | /** |
| | | * This class is used to access CLI profile annotations. |
| | | */ |
| | | final class CLIProfile { |
| | | |
| | | /** The singleton instance. */ |
| | | private static final CLIProfile INSTANCE = new CLIProfile(); |
| | | |
| | | /** |
| | | * Get the CLI profile instance. |
| | | * |
| | | * @return Returns the CLI profile instance. |
| | | */ |
| | | public static CLIProfile getInstance() { |
| | | return INSTANCE; |
| | | } |
| | | |
| | | /** The CLI profile property table. */ |
| | | private final ManagedObjectDefinitionResource resource; |
| | | |
| | | /** Private constructor. */ |
| | | private CLIProfile() { |
| | | this.resource = ManagedObjectDefinitionResource.createForProfile("cli"); |
| | | } |
| | | |
| | | /** |
| | | * Gets the default set of properties which should be displayed in a list-xxx operation. |
| | | * |
| | | * @param r |
| | | * The relation definition. |
| | | * @return Returns the default set of properties which should be displayed in a list-xxx operation. |
| | | */ |
| | | public Set<String> getDefaultListPropertyNames(RelationDefinition<?, ?> r) { |
| | | final String s = resource.getString(r.getParentDefinition(), "relation." + r.getName() + ".list-properties"); |
| | | if (s.trim().length() == 0) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return new LinkedHashSet<String>(Arrays.asList(s.split(","))); |
| | | } |
| | | |
| | | /** |
| | | * Gets the naming argument which should be used for a relation definition. |
| | | * |
| | | * @param r |
| | | * The relation definition. |
| | | * @return Returns the naming argument which should be used for a relation definition. |
| | | */ |
| | | public String getNamingArgument(RelationDefinition<?, ?> r) { |
| | | String s = resource.getString(r.getParentDefinition(), "relation." + r.getName() + ".naming-argument-override") |
| | | .trim(); |
| | | |
| | | if (s.length() == 0) { |
| | | // Use the last word in the managed object name as the argument |
| | | // prefix. |
| | | StringBuilder builder = new StringBuilder(); |
| | | s = r.getChildDefinition().getName(); |
| | | int i = s.lastIndexOf('-'); |
| | | if (i < 0 || i == (s.length() - 1)) { |
| | | builder.append(s); |
| | | } else { |
| | | builder.append(s.substring(i + 1)); |
| | | } |
| | | |
| | | if (r instanceof SetRelationDefinition) { |
| | | // Set relations are named using their type, so be consistent |
| | | // with their associated create-xxx sub-command. |
| | | builder.append("-type"); |
| | | } else { |
| | | // Other relations (instantiable) are named by the user. |
| | | builder.append("-name"); |
| | | } |
| | | |
| | | s = builder.toString(); |
| | | } |
| | | |
| | | return s; |
| | | } |
| | | |
| | | /** |
| | | * Determines if instances of the specified managed object definition are to be used for customization. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @return Returns <code>true</code> if instances of the specified managed object definition are to be used for |
| | | * customization. |
| | | */ |
| | | public boolean isForCustomization(AbstractManagedObjectDefinition<?, ?> d) { |
| | | return Boolean.parseBoolean(resource.getString(d, "is-for-customization")); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013-2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.LIST_TABLE_SEPARATOR; |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayMissingMandatoryPropertyException; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayOperationRejectedException; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.SortedMap; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.AggregationPropertyDefinition; |
| | | import org.forgerock.opendj.config.Configuration; |
| | | import org.forgerock.opendj.config.ConfigurationClient; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectAlreadyExistsException; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectOption; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; |
| | | import org.forgerock.opendj.config.PropertyException; |
| | | import org.forgerock.opendj.config.PropertyOption; |
| | | import org.forgerock.opendj.config.PropertyProvider; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.IllegalManagedObjectNameException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException; |
| | | import org.forgerock.opendj.config.client.OperationRejectedException; |
| | | import org.forgerock.opendj.config.conditions.Condition; |
| | | import org.forgerock.opendj.config.conditions.ContainsCondition; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.Argument; |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.HelpCallback; |
| | | import com.forgerock.opendj.cli.MenuBuilder; |
| | | 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.TableBuilder; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | import com.forgerock.opendj.cli.ValidationCallback; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to create new managed objects. |
| | | * <p> |
| | | * This sub-command implements the various create-xxx sub-commands. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | */ |
| | | final class CreateSubCommandHandler<C extends ConfigurationClient, S extends Configuration> extends SubCommandHandler { |
| | | |
| | | /** |
| | | * A property provider which uses the command-line arguments to provide initial property values. |
| | | */ |
| | | private static class MyPropertyProvider implements PropertyProvider { |
| | | |
| | | /** Decoded set of properties. */ |
| | | private final Map<PropertyDefinition<?>, Collection<?>> properties |
| | | = new HashMap<PropertyDefinition<?>, Collection<?>>(); |
| | | |
| | | /** |
| | | * Create a new property provider using the provided set of property value arguments. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @param namingPropertyDefinition |
| | | * The naming property definition if there is one. |
| | | * @param args |
| | | * The property value arguments. |
| | | * @throws ArgumentException |
| | | * If the property value arguments could not be parsed. |
| | | */ |
| | | public MyPropertyProvider(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> namingPropertyDefinition, |
| | | List<String> args) throws ArgumentException { |
| | | for (String s : args) { |
| | | // Parse the property "property:value". |
| | | int sep = s.indexOf(':'); |
| | | |
| | | if (sep < 0) { |
| | | throw ArgumentExceptionFactory.missingSeparatorInPropertyArgument(s); |
| | | } |
| | | |
| | | if (sep == 0) { |
| | | throw ArgumentExceptionFactory.missingNameInPropertyArgument(s); |
| | | } |
| | | |
| | | String propertyName = s.substring(0, sep); |
| | | String value = s.substring(sep + 1, s.length()); |
| | | if (value.length() == 0) { |
| | | throw ArgumentExceptionFactory.missingValueInPropertyArgument(s); |
| | | } |
| | | |
| | | // Check the property definition. |
| | | PropertyDefinition<?> pd; |
| | | try { |
| | | pd = d.getPropertyDefinition(propertyName); |
| | | } catch (IllegalArgumentException e) { |
| | | throw ArgumentExceptionFactory.unknownProperty(d, propertyName); |
| | | } |
| | | |
| | | // Make sure that the user is not attempting to set the naming |
| | | // property. |
| | | if (pd.equals(namingPropertyDefinition)) { |
| | | throw ArgumentExceptionFactory.unableToSetNamingProperty(d, pd); |
| | | } |
| | | |
| | | // Add the value. |
| | | addPropertyValue(d, pd, value); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get the set of parsed property definitions that have values specified. |
| | | * |
| | | * @return Returns the set of parsed property definitions that have values specified. |
| | | */ |
| | | public Set<PropertyDefinition<?>> getProperties() { |
| | | return properties.keySet(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @SuppressWarnings("unchecked") |
| | | public <T> Collection<T> getPropertyValues(PropertyDefinition<T> d) { |
| | | Collection<T> values = (Collection<T>) properties.get(d); |
| | | if (values == null) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return values; |
| | | } |
| | | |
| | | /** Add a single property value. */ |
| | | @SuppressWarnings("unchecked") |
| | | private <T> void addPropertyValue(ManagedObjectDefinition<?, ?> d, PropertyDefinition<T> pd, String s) |
| | | throws ArgumentException { |
| | | T value; |
| | | try { |
| | | value = pd.decodeValue(s); |
| | | } catch (PropertyException e) { |
| | | throw ArgumentExceptionFactory.adaptPropertyException(e, d); |
| | | } |
| | | |
| | | Collection<T> values = (Collection<T>) properties.get(pd); |
| | | if (values == null) { |
| | | values = new LinkedList<T>(); |
| | | } |
| | | values.add(value); |
| | | |
| | | if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | PropertyException e = PropertyException.propertyIsSingleValuedException(pd); |
| | | throw ArgumentExceptionFactory.adaptPropertyException(e, d); |
| | | } |
| | | |
| | | properties.put(pd, values); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A help call-back which displays help about available component types. |
| | | */ |
| | | private static final class TypeHelpCallback<C extends ConfigurationClient, S extends Configuration> implements |
| | | HelpCallback { |
| | | |
| | | /** The abstract definition for which to provide help on its sub-types. */ |
| | | private final AbstractManagedObjectDefinition<C, S> d; |
| | | |
| | | /** Create a new type help call-back. */ |
| | | private TypeHelpCallback(AbstractManagedObjectDefinition<C, S> d) { |
| | | this.d = d; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public void display(ConsoleApplication app) { |
| | | app.println(INFO_DSCFG_CREATE_TYPE_HELP_HEADING.get(d.getUserFriendlyPluralName())); |
| | | |
| | | app.println(); |
| | | app.println(d.getSynopsis()); |
| | | |
| | | if (d.getDescription() != null) { |
| | | app.println(); |
| | | app.println(d.getDescription()); |
| | | } |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | |
| | | // Create a table containing a description of each component |
| | | // type. |
| | | TableBuilder builder = new TableBuilder(); |
| | | |
| | | builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE.get()); |
| | | |
| | | builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR.get()); |
| | | |
| | | boolean isFirst = true; |
| | | for (ManagedObjectDefinition<?, ?> mod : getSubTypes(d).values()) { |
| | | // Only display advanced types and custom types in advanced mode. |
| | | if (!app.isAdvancedMode()) { |
| | | if (mod.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | if (CLIProfile.getInstance().isForCustomization(mod)) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | LocalizableMessage ufn = mod.getUserFriendlyName(); |
| | | LocalizableMessage synopsis = mod.getSynopsis(); |
| | | LocalizableMessage description = mod.getDescription(); |
| | | if (CLIProfile.getInstance().isForCustomization(mod)) { |
| | | ufn = INFO_DSCFG_CUSTOM_TYPE_OPTION.get(ufn); |
| | | synopsis = INFO_DSCFG_CUSTOM_TYPE_SYNOPSIS.get(ufn); |
| | | description = null; |
| | | } else if (mod == d) { |
| | | ufn = INFO_DSCFG_GENERIC_TYPE_OPTION.get(ufn); |
| | | synopsis = INFO_DSCFG_GENERIC_TYPE_SYNOPSIS.get(ufn); |
| | | description = null; |
| | | } |
| | | |
| | | if (!isFirst) { |
| | | builder.startRow(); |
| | | builder.startRow(); |
| | | } else { |
| | | isFirst = false; |
| | | } |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(ufn); |
| | | builder.appendCell(synopsis); |
| | | if (description != null) { |
| | | builder.startRow(); |
| | | builder.startRow(); |
| | | builder.appendCell(); |
| | | builder.appendCell(description); |
| | | } |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setColumnWidth(1, 0); |
| | | printer.setColumnSeparator(LIST_TABLE_SEPARATOR); |
| | | builder.print(printer); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * The value for the long option set. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_SET = "set"; |
| | | |
| | | /** |
| | | * The value for the long option type. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_TYPE = "type"; |
| | | |
| | | /** |
| | | * The value for the short option property. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_SET = null; |
| | | |
| | | /** |
| | | * The value for the short option type. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_TYPE = 't'; |
| | | |
| | | /** |
| | | * The value for the long option remove (this is used only internally). |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_REMOVE = "remove"; |
| | | |
| | | /** |
| | | * The value for the long option reset (this is used only internally). |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_RESET = "reset"; |
| | | |
| | | /** |
| | | * Creates a new create-xxx sub-command for an instantiable relation. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The instantiable relation. |
| | | * @return Returns the new create-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create( |
| | | SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> r) |
| | | throws ArgumentException { |
| | | return new CreateSubCommandHandler<C, S>(parser, p, r, r.getNamingPropertyDefinition(), p.child(r, "DUMMY")); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new create-xxx sub-command for a sets relation. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The set relation. |
| | | * @return Returns the new create-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create( |
| | | SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, SetRelationDefinition<C, S> r) |
| | | throws ArgumentException { |
| | | return new CreateSubCommandHandler<C, S>(parser, p, r, null, p.child(r)); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new create-xxx sub-command for an optional relation. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The optional relation. |
| | | * @return Returns the new create-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create( |
| | | SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, OptionalRelationDefinition<C, S> r) |
| | | throws ArgumentException { |
| | | return new CreateSubCommandHandler<C, S>(parser, p, r, null, p.child(r)); |
| | | } |
| | | |
| | | /** |
| | | * Interactively lets a user create a new managed object beneath a parent. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | * @param app |
| | | * The console application. |
| | | * @param context |
| | | * The management context. |
| | | * @param parent |
| | | * The parent managed object. |
| | | * @param rd |
| | | * The relation beneath which the child managed object should be created. |
| | | * @return Returns a MenuResult.success() containing the name of the created managed object if it was created |
| | | * successfully, or MenuResult.quit(), or MenuResult.cancel(), if the managed object was edited |
| | | * interactively and the user chose to quit or cancel. |
| | | * @throws ClientException |
| | | * If an unrecoverable client exception occurred whilst interacting with the server. |
| | | */ |
| | | public static <C extends ConfigurationClient, S extends Configuration> MenuResult<String> createManagedObject( |
| | | ConsoleApplication app, ManagementContext context, ManagedObject<?> parent, |
| | | InstantiableRelationDefinition<C, S> rd) throws ClientException { |
| | | return createManagedObject(app, context, parent, rd, null); |
| | | } |
| | | |
| | | /** |
| | | * Interactively lets a user create a new managed object beneath a parent. |
| | | * |
| | | * @param <C> |
| | | * The type of managed object which can be created. |
| | | * @param <S> |
| | | * The type of server managed object which can be created. |
| | | * @param app |
| | | * The console application. |
| | | * @param context |
| | | * The management context. |
| | | * @param parent |
| | | * The parent managed object. |
| | | * @param rd |
| | | * The relation beneath which the child managed object should be created. |
| | | * @param handler |
| | | * The subcommand handler whose command builder must be updated. |
| | | * @return Returns a MenuResult.success() containing the name of the created managed object if it was created |
| | | * successfully, or MenuResult.quit(), or MenuResult.cancel(), if the managed object was edited |
| | | * interactively and the user chose to quit or cancel. |
| | | * @throws ClientException |
| | | * If an unrecoverable client exception occurred whilst interacting with the server. |
| | | * @throws ClientException |
| | | * If an error occurred whilst interacting with the console. |
| | | */ |
| | | private static <C extends ConfigurationClient, S extends Configuration> MenuResult<String> createManagedObject( |
| | | ConsoleApplication app, ManagementContext context, ManagedObject<?> parent, |
| | | InstantiableRelationDefinition<C, S> rd, SubCommandHandler handler) throws ClientException { |
| | | AbstractManagedObjectDefinition<C, S> d = rd.getChildDefinition(); |
| | | |
| | | // First determine what type of component the user wants to create. |
| | | MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result; |
| | | result = getTypeInteractively(app, d, Collections.<String> emptySet()); |
| | | |
| | | ManagedObjectDefinition<? extends C, ? extends S> mod; |
| | | if (result.isSuccess()) { |
| | | mod = result.getValue(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | |
| | | // Now create the component. |
| | | // FIXME: handle default value exceptions? |
| | | List<PropertyException> exceptions = new LinkedList<PropertyException>(); |
| | | app.println(); |
| | | app.println(); |
| | | ManagedObject<? extends C> mo = createChildInteractively(app, parent, rd, mod, exceptions); |
| | | |
| | | // Let the user interactively configure the managed object and commit it. |
| | | MenuResult<Void> result2 = commitManagedObject(app, context, mo, handler); |
| | | if (result2.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else if (result2.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else { |
| | | return MenuResult.success(mo.getManagedObjectPath().getName()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check that any referenced components are enabled if required. |
| | | */ |
| | | private static MenuResult<Void> checkReferences(ConsoleApplication app, ManagementContext context, |
| | | ManagedObject<?> mo, SubCommandHandler handler) throws ClientException { |
| | | ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | |
| | | try { |
| | | for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { |
| | | if (pd instanceof AggregationPropertyDefinition<?, ?>) { |
| | | AggregationPropertyDefinition<?, ?> apd = (AggregationPropertyDefinition<?, ?>) pd; |
| | | |
| | | // Skip this aggregation if the referenced managed objects |
| | | // do not need to be enabled. |
| | | if (!apd.getTargetNeedsEnablingCondition().evaluate(context, mo)) { |
| | | continue; |
| | | } |
| | | |
| | | // The referenced component(s) must be enabled. |
| | | for (String name : mo.getPropertyValues(apd)) { |
| | | ManagedObjectPath<?, ?> path = apd.getChildPath(name); |
| | | LocalizableMessage rufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | ManagedObject<?> ref; |
| | | try { |
| | | ref = context.getManagedObject(path); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(rufn, rufn, rufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // FIXME: should not abort here. Instead, display the |
| | | // errors (if verbose) and apply the changes to the |
| | | // partial managed object. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(rufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(rufn); |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | |
| | | Condition condition = apd.getTargetIsEnabledCondition(); |
| | | while (!condition.evaluate(context, ref)) { |
| | | boolean isBadReference = true; |
| | | |
| | | if (condition instanceof ContainsCondition) { |
| | | // Attempt to automatically enable the managed object. |
| | | ContainsCondition cvc = (ContainsCondition) condition; |
| | | app.println(); |
| | | if (app.confirmAction( |
| | | INFO_EDITOR_PROMPT_ENABLED_REFERENCED_COMPONENT.get(rufn, name, ufn), true)) { |
| | | cvc.setPropertyValue(ref); |
| | | try { |
| | | ref.commit(); |
| | | isBadReference = false; |
| | | } catch (MissingMandatoryPropertiesException e) { |
| | | // Give the user the chance to fix the problems. |
| | | app.println(); |
| | | displayMissingMandatoryPropertyException(app, e); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) { |
| | | MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app, |
| | | context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (OperationRejectedException e) { |
| | | // Give the user the chance to fix the problems. |
| | | app.println(); |
| | | displayOperationRejectedException(app, e); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) { |
| | | MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app, |
| | | context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } catch (ManagedObjectAlreadyExistsException e) { |
| | | // Should never happen. |
| | | throw new IllegalStateException(e); |
| | | } |
| | | } |
| | | } else { |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_TO_ENABLE.get(rufn, name, ufn), true)) { |
| | | MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app, |
| | | context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If the referenced component is still disabled because |
| | | // the user refused to modify it, then give the used the |
| | | // option of editing the referencing component. |
| | | if (isBadReference) { |
| | | app.println(); |
| | | app.println(ERR_SET_REFERENCED_COMPONENT_DISABLED.get(ufn, rufn)); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.again(); |
| | | } |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } |
| | | |
| | | return MenuResult.success(); |
| | | } |
| | | |
| | | /** Commit a new managed object's configuration. */ |
| | | private static MenuResult<Void> commitManagedObject(ConsoleApplication app, ManagementContext context, |
| | | ManagedObject<?> mo, SubCommandHandler handler) throws ClientException { |
| | | ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | |
| | | PropertyValueEditor editor = new PropertyValueEditor(app, context); |
| | | while (true) { |
| | | // Interactively set properties if applicable. |
| | | if (app.isInteractive()) { |
| | | SortedSet<PropertyDefinition<?>> properties = new TreeSet<PropertyDefinition<?>>(); |
| | | for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { |
| | | if (pd.hasOption(PropertyOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | properties.add(pd); |
| | | } |
| | | |
| | | MenuResult<Void> result = editor.edit(mo, properties, true); |
| | | |
| | | // Interactively enable/edit referenced components. |
| | | if (result.isSuccess()) { |
| | | result = checkReferences(app, context, mo, handler); |
| | | if (result.isAgain()) { |
| | | // Edit again. |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to cancel any changes. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | try { |
| | | // Create the managed object. |
| | | mo.commit(); |
| | | |
| | | // Output success message. |
| | | if (app.isInteractive() || app.isVerbose()) { |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(ufn)); |
| | | } |
| | | |
| | | if (handler != null) { |
| | | for (PropertyEditorModification<?> mod : editor.getModifications()) { |
| | | try { |
| | | Argument arg = createArgument(mod); |
| | | handler.getCommandBuilder().addArgument(arg); |
| | | } catch (ArgumentException ae) { |
| | | // This is a bug |
| | | throw new RuntimeException("Unexpected error generating the command builder: " + ae, ae); |
| | | } |
| | | } |
| | | handler.setCommandBuilderUseful(true); |
| | | } |
| | | return MenuResult.success(); |
| | | } catch (MissingMandatoryPropertiesException e) { |
| | | if (app.isInteractive()) { |
| | | // If interactive, give the user the chance to fix the |
| | | // problems. |
| | | app.println(); |
| | | displayMissingMandatoryPropertyException(app, e); |
| | | app.println(); |
| | | if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } else { |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e); |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (OperationRejectedException e) { |
| | | if (app.isInteractive()) { |
| | | // If interactive, give the user the chance to fix the |
| | | // problems. |
| | | app.println(); |
| | | displayOperationRejectedException(app, e); |
| | | app.println(); |
| | | if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } else { |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e); |
| | | } |
| | | } catch (ErrorResultException e) { |
| | | final LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage()); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } |
| | | } catch (ManagedObjectAlreadyExistsException e) { |
| | | final LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_MOAEE.get(ufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.ENTRY_ALREADY_EXISTS, msg); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** Interactively create the child by prompting for the name. */ |
| | | private static <C extends ConfigurationClient, S |
| | | extends Configuration> ManagedObject<? extends C> createChildInteractively( |
| | | ConsoleApplication app, final ManagedObject<?> parent, |
| | | final InstantiableRelationDefinition<C, S> irelation, |
| | | final ManagedObjectDefinition<? extends C, ? extends S> d, final List<PropertyException> exceptions) |
| | | throws ClientException { |
| | | |
| | | ValidationCallback<ManagedObject<? extends C>> validator |
| | | = new ValidationCallback<ManagedObject<? extends C>>() { |
| | | |
| | | public ManagedObject<? extends C> validate(ConsoleApplication app, String input) |
| | | throws ClientException { |
| | | ManagedObject<? extends C> child; |
| | | |
| | | // First attempt to create the child, this will guarantee that |
| | | // the name is acceptable. |
| | | try { |
| | | child = parent.createChild(irelation, d, input, exceptions); |
| | | } catch (IllegalManagedObjectNameException e) { |
| | | ClientException ae = ArgumentExceptionFactory.adaptIllegalManagedObjectNameException(e, d); |
| | | app.println(); |
| | | app.println(ae.getMessageObject()); |
| | | app.println(); |
| | | return null; |
| | | } |
| | | |
| | | // Make sure that there are not any other children with the |
| | | // same name. |
| | | try { |
| | | // Attempt to retrieve a child using this name. |
| | | parent.getChild(irelation, input); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation.getUserFriendlyName()); |
| | | throw new ClientException(ReturnCode.ERROR_USER_DATA, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(irelation.getUserFriendlyName()); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(irelation.getUserFriendlyName(), |
| | | e.getMessage()); |
| | | throw new ClientException(ReturnCode.APPLICATION_ERROR, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | // Do nothing. |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // Do nothing. |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // The child does not already exist so this name is ok. |
| | | return child; |
| | | } |
| | | |
| | | // A child with the specified name must already exist. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get( |
| | | irelation.getUserFriendlyName(), input); |
| | | app.println(); |
| | | app.println(msg); |
| | | app.println(); |
| | | return null; |
| | | } |
| | | }; |
| | | |
| | | // Display additional help if the name is a naming property. |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | PropertyDefinition<?> pd = irelation.getNamingPropertyDefinition(); |
| | | if (pd != null) { |
| | | app.println(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING.get(ufn, pd.getName())); |
| | | |
| | | app.println(); |
| | | app.errPrintln(pd.getSynopsis(), 4); |
| | | |
| | | if (pd.getDescription() != null) { |
| | | app.println(); |
| | | app.errPrintln(pd.getDescription(), 4); |
| | | } |
| | | |
| | | PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true); |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_EDITOR_HEADING_SYNTAX.get()); |
| | | builder.appendCell(b.getUsage(pd)); |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setIndentWidth(4); |
| | | printer.setColumnWidth(1, 0); |
| | | |
| | | app.println(); |
| | | builder.print(printer); |
| | | app.println(); |
| | | |
| | | return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_CONT.get(ufn), validator); |
| | | } else { |
| | | return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT.get(ufn), validator); |
| | | } |
| | | } |
| | | |
| | | /** Interactively ask the user which type of component they want to create. */ |
| | | private static <C extends ConfigurationClient, S extends Configuration> MenuResult |
| | | <ManagedObjectDefinition<? extends C, ? extends S>> getTypeInteractively( |
| | | ConsoleApplication app, AbstractManagedObjectDefinition<C, S> d, Set<String> prohibitedTypes) |
| | | throws ClientException { |
| | | // First get the list of available of sub-types. |
| | | List<ManagedObjectDefinition<? extends C, ? extends S>> filteredTypes |
| | | = new LinkedList<ManagedObjectDefinition<? extends C, ? extends S>>(getSubTypes(d).values()); |
| | | boolean isOnlyOneType = filteredTypes.size() == 1; |
| | | |
| | | Iterator<ManagedObjectDefinition<? extends C, ? extends S>> i; |
| | | for (i = filteredTypes.iterator(); i.hasNext();) { |
| | | ManagedObjectDefinition<? extends C, ? extends S> cd = i.next(); |
| | | |
| | | if (prohibitedTypes.contains(cd.getName())) { |
| | | // Remove filtered types. |
| | | i.remove(); |
| | | } else if (!app.isAdvancedMode()) { |
| | | // Only display advanced types and custom types in advanced mode. |
| | | if (cd.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | i.remove(); |
| | | } else if (CLIProfile.getInstance().isForCustomization(cd)) { |
| | | i.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If there is only one choice then return immediately. |
| | | if (filteredTypes.size() == 0) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_NO_AVAILABLE_TYPES.get(d.getUserFriendlyName()); |
| | | app.println(msg); |
| | | return MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> cancel(); |
| | | } else if (filteredTypes.size() == 1) { |
| | | ManagedObjectDefinition<? extends C, ? extends S> type = filteredTypes.iterator().next(); |
| | | if (!isOnlyOneType) { |
| | | // Only one option available so confirm that the user wishes to |
| | | // use it. |
| | | LocalizableMessage msg = INFO_DSCFG_TYPE_PROMPT_SINGLE.get(d.getUserFriendlyName(), |
| | | type.getUserFriendlyName()); |
| | | if (!app.confirmAction(msg, true)) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | return MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> success(type); |
| | | } else { |
| | | MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>> builder |
| | | = new MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>>(app); |
| | | LocalizableMessage msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(d.getUserFriendlyName()); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | builder.setPrompt(msg); |
| | | |
| | | for (ManagedObjectDefinition<? extends C, ? extends S> mod : filteredTypes) { |
| | | |
| | | LocalizableMessage option = mod.getUserFriendlyName(); |
| | | if (CLIProfile.getInstance().isForCustomization(mod)) { |
| | | option = INFO_DSCFG_CUSTOM_TYPE_OPTION.get(option); |
| | | } else if (mod == d) { |
| | | option = INFO_DSCFG_GENERIC_TYPE_OPTION.get(option); |
| | | } |
| | | builder.addNumberedOption(option, |
| | | MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> success(mod)); |
| | | } |
| | | builder.addHelpOption(new TypeHelpCallback<C, S>(d)); |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(true); |
| | | } |
| | | builder.addQuitOption(); |
| | | return builder.toMenu().run(); |
| | | } |
| | | } |
| | | |
| | | /** The sub-commands naming arguments. */ |
| | | private final List<StringArgument> namingArgs; |
| | | |
| | | /** The optional naming property definition. */ |
| | | private final PropertyDefinition<?> namingPropertyDefinition; |
| | | |
| | | /** The path of the parent managed object. */ |
| | | private final ManagedObjectPath<?, ?> path; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property values. |
| | | */ |
| | | private final StringArgument propertySetArgument; |
| | | |
| | | /** The relation which should be used for creating children. */ |
| | | private final RelationDefinition<C, S> relation; |
| | | |
| | | /** The sub-command associated with this handler. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** |
| | | * The argument which should be used to specify the type of managed object to be created. |
| | | */ |
| | | private final StringArgument typeArgument; |
| | | |
| | | /** |
| | | * The set of instantiable managed object definitions and their associated type option value. |
| | | */ |
| | | private final SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> types; |
| | | |
| | | /** The syntax of the type argument. */ |
| | | private final String typeUsage; |
| | | |
| | | /** Common constructor. */ |
| | | private CreateSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | RelationDefinition<C, S> r, PropertyDefinition<?> pd, ManagedObjectPath<?, ?> c) throws ArgumentException { |
| | | this.path = p; |
| | | this.relation = r; |
| | | this.namingPropertyDefinition = pd; |
| | | |
| | | // Create the sub-command. |
| | | String name = "create-" + r.getName(); |
| | | LocalizableMessage description = INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE.get(r.getChildDefinition() |
| | | .getUserFriendlyPluralName()); |
| | | this.subCommand = new SubCommand(parser, name, false, 0, 0, null, description); |
| | | |
| | | // Create the -t argument which is used to specify the type of |
| | | // managed object to be created. |
| | | this.types = getSubTypes(r.getChildDefinition()); |
| | | |
| | | // Create the naming arguments. |
| | | this.namingArgs = createNamingArgs(subCommand, c, true); |
| | | |
| | | // Build the -t option usage. |
| | | this.typeUsage = getSubTypesUsage(r.getChildDefinition()); |
| | | |
| | | // Create the --property argument which is used to specify |
| | | // property values. |
| | | this.propertySetArgument = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, |
| | | OPTION_DSCFG_LONG_SET, false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | this.subCommand.addArgument(this.propertySetArgument); |
| | | |
| | | if (!types.containsKey(DSConfig.GENERIC_TYPE)) { |
| | | // The option is mandatory when non-interactive. |
| | | this.typeArgument = new StringArgument("type", OPTION_DSCFG_SHORT_TYPE, OPTION_DSCFG_LONG_TYPE, false, |
| | | false, true, INFO_TYPE_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_TYPE.get(r |
| | | .getChildDefinition().getUserFriendlyName(), typeUsage)); |
| | | } else { |
| | | // The option has a sensible default "generic". |
| | | this.typeArgument = new StringArgument("type", OPTION_DSCFG_SHORT_TYPE, OPTION_DSCFG_LONG_TYPE, false, |
| | | false, true, INFO_TYPE_PLACEHOLDER.get(), DSConfig.GENERIC_TYPE, null, |
| | | INFO_DSCFG_DESCRIPTION_TYPE_DEFAULT.get(r.getChildDefinition().getUserFriendlyName(), |
| | | DSConfig.GENERIC_TYPE, typeUsage)); |
| | | |
| | | // Hide the option if it defaults to generic and generic is the |
| | | // only possible value. |
| | | if (types.size() == 1) { |
| | | this.typeArgument.setHidden(true); |
| | | } |
| | | } |
| | | this.subCommand.addArgument(this.typeArgument); |
| | | |
| | | // Register the tags associated with the child managed objects. |
| | | addTags(relation.getChildDefinition().getAllTags()); |
| | | } |
| | | |
| | | /** |
| | | * Gets the relation definition associated with the type of component that this sub-command handles. |
| | | * |
| | | * @return Returns the relation definition associated with the type of component that this sub-command handles. |
| | | */ |
| | | public RelationDefinition<?, ?> getRelationDefinition() { |
| | | return relation; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | LocalizableMessage ufn = relation.getUserFriendlyName(); |
| | | |
| | | // Get the naming argument values. |
| | | List<String> names = getNamingArgValues(app, namingArgs); |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | |
| | | setCommandBuilderUseful(false); |
| | | |
| | | // Update the command builder. |
| | | updateCommandBuilderWithSubCommand(); |
| | | |
| | | // Add the child managed object. |
| | | ManagementContext context = factory.getManagementContext(app); |
| | | MenuResult<ManagedObject<?>> result; |
| | | try { |
| | | result = getManagedObject(app, context, path, names); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(pufn, pufn, pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(pufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to cancel creation. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | // Must be menu driven, so no need for error message. |
| | | return MenuResult.cancel(); |
| | | } |
| | | |
| | | ManagedObject<?> parent = result.getValue(); |
| | | |
| | | // Determine the type of managed object to be created. If we are creating |
| | | // a managed object beneath a set relation then prevent creation of |
| | | // duplicates. |
| | | Set<String> prohibitedTypes; |
| | | if (relation instanceof SetRelationDefinition) { |
| | | SetRelationDefinition<C, S> sr = (SetRelationDefinition<C, S>) relation; |
| | | prohibitedTypes = new HashSet<String>(); |
| | | try { |
| | | for (String child : parent.listChildren(sr)) { |
| | | prohibitedTypes.add(child); |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } |
| | | } else { |
| | | // No prohibited types. |
| | | prohibitedTypes = Collections.emptySet(); |
| | | } |
| | | |
| | | ManagedObjectDefinition<? extends C, ? extends S> d; |
| | | if (!typeArgument.isPresent()) { |
| | | if (app.isInteractive()) { |
| | | // Let the user choose. |
| | | MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> dresult; |
| | | app.println(); |
| | | app.println(); |
| | | dresult = getTypeInteractively(app, relation.getChildDefinition(), prohibitedTypes); |
| | | |
| | | if (dresult.isSuccess()) { |
| | | d = dresult.getValue(); |
| | | } else if (dresult.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | // Must be quit. |
| | | if (!app.isMenuDrivenMode()) { |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } |
| | | } else if (typeArgument.getDefaultValue() != null) { |
| | | d = types.get(typeArgument.getDefaultValue()); |
| | | } else { |
| | | throw ArgumentExceptionFactory.missingMandatoryNonInteractiveArgument(typeArgument); |
| | | } |
| | | } else { |
| | | d = types.get(typeArgument.getValue()); |
| | | if (d == null) { |
| | | throw ArgumentExceptionFactory.unknownSubType(relation, typeArgument.getValue(), typeUsage); |
| | | } |
| | | } |
| | | |
| | | // Encode the provided properties. |
| | | List<String> propertyArgs = propertySetArgument.getValues(); |
| | | MyPropertyProvider provider = new MyPropertyProvider(d, namingPropertyDefinition, propertyArgs); |
| | | |
| | | ManagedObject<? extends C> child; |
| | | List<PropertyException> exceptions = new LinkedList<PropertyException>(); |
| | | boolean isNameProvidedInteractively = false; |
| | | String providedNamingArgName = null; |
| | | if (relation instanceof InstantiableRelationDefinition) { |
| | | InstantiableRelationDefinition<C, S> irelation = (InstantiableRelationDefinition<C, S>) relation; |
| | | String name = names.get(names.size() - 1); |
| | | if (name == null) { |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.println(); |
| | | child = createChildInteractively(app, parent, irelation, d, exceptions); |
| | | isNameProvidedInteractively = true; |
| | | providedNamingArgName = CLIProfile.getInstance().getNamingArgument(irelation); |
| | | } else { |
| | | throw ArgumentExceptionFactory |
| | | .missingMandatoryNonInteractiveArgument(namingArgs.get(names.size() - 1)); |
| | | } |
| | | } else { |
| | | try { |
| | | child = parent.createChild(irelation, d, name, exceptions); |
| | | } catch (IllegalManagedObjectNameException e) { |
| | | throw ArgumentExceptionFactory.adaptIllegalManagedObjectNameException(e, d); |
| | | } |
| | | } |
| | | } else if (relation instanceof SetRelationDefinition) { |
| | | SetRelationDefinition<C, S> srelation = (SetRelationDefinition<C, S>) relation; |
| | | child = parent.createChild(srelation, d, exceptions); |
| | | } else { |
| | | OptionalRelationDefinition<C, S> orelation = (OptionalRelationDefinition<C, S>) relation; |
| | | child = parent.createChild(orelation, d, exceptions); |
| | | } |
| | | |
| | | // FIXME: display any default behavior exceptions in verbose |
| | | // mode. |
| | | |
| | | // Set any properties specified on the command line. |
| | | for (PropertyDefinition<?> pd : provider.getProperties()) { |
| | | setProperty(child, provider, pd); |
| | | } |
| | | |
| | | // Now the command line changes have been made, create the managed |
| | | // object interacting with the user to fix any problems if |
| | | // required. |
| | | MenuResult<Void> result2 = commitManagedObject(app, context, child, this); |
| | | if (result2.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else if (result2.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else { |
| | | if (typeArgument.hasValue()) { |
| | | getCommandBuilder().addArgument(typeArgument); |
| | | } else { |
| | | // Set the type provided by the user |
| | | StringArgument arg = new StringArgument(typeArgument.getName(), OPTION_DSCFG_SHORT_TYPE, |
| | | OPTION_DSCFG_LONG_TYPE, false, false, true, INFO_TYPE_PLACEHOLDER.get(), |
| | | typeArgument.getDefaultValue(), typeArgument.getPropertyName(), typeArgument.getDescription()); |
| | | arg.addValue(getTypeName(d)); |
| | | getCommandBuilder().addArgument(arg); |
| | | } |
| | | if (propertySetArgument.hasValue()) { |
| | | /* |
| | | * We might have some conflicts in terms of arguments: the user might have provided some values that |
| | | * were not good and then these have overwritten when asking for them interactively: filter them |
| | | */ |
| | | StringArgument filteredArg = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, |
| | | OPTION_DSCFG_LONG_SET, false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | for (String value : propertySetArgument.getValues()) { |
| | | boolean addValue = true; |
| | | int index = value.indexOf(':'); |
| | | if (index != -1) { |
| | | String propName = value.substring(0, index); |
| | | for (Argument arg : getCommandBuilder().getArguments()) { |
| | | for (String value2 : arg.getValues()) { |
| | | String prop2Name; |
| | | if (OPTION_DSCFG_LONG_SET.equals(arg.getName()) |
| | | || OPTION_DSCFG_LONG_REMOVE.equals(arg.getName())) { |
| | | int index2 = value2.indexOf(':'); |
| | | if (index2 != -1) { |
| | | prop2Name = value2.substring(0, index2); |
| | | } else { |
| | | prop2Name = null; |
| | | } |
| | | } else if (OPTION_DSCFG_LONG_RESET.equals(arg.getName())) { |
| | | prop2Name = value2; |
| | | } else { |
| | | prop2Name = null; |
| | | } |
| | | if (prop2Name != null && prop2Name.equalsIgnoreCase(propName)) { |
| | | addValue = false; |
| | | break; |
| | | } |
| | | } |
| | | if (!addValue) { |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | addValue = false; |
| | | } |
| | | if (addValue) { |
| | | filteredArg.addValue(value); |
| | | } |
| | | } |
| | | if (filteredArg.hasValue()) { |
| | | getCommandBuilder().addArgument(filteredArg); |
| | | } |
| | | } |
| | | |
| | | /* Filter the arguments that are used internally */ |
| | | List<Argument> argsCopy = new LinkedList<Argument>(getCommandBuilder().getArguments()); |
| | | for (Argument arg : argsCopy) { |
| | | if (arg != null |
| | | && (OPTION_DSCFG_LONG_RESET.equals(arg.getName()) || OPTION_DSCFG_LONG_REMOVE.equals(arg |
| | | .getName()))) { |
| | | getCommandBuilder().removeArgument(arg); |
| | | } |
| | | } |
| | | |
| | | if (isNameProvidedInteractively) { |
| | | StringArgument arg = new StringArgument(providedNamingArgName, null, providedNamingArgName, false, |
| | | true, INFO_NAME_PLACEHOLDER.get(), INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d |
| | | .getUserFriendlyName())); |
| | | arg.addValue(child.getManagedObjectPath().getName()); |
| | | getCommandBuilder().addArgument(arg); |
| | | } else { |
| | | for (StringArgument arg : namingArgs) { |
| | | if (arg.isPresent()) { |
| | | getCommandBuilder().addArgument(arg); |
| | | } |
| | | } |
| | | } |
| | | return MenuResult.success(0); |
| | | } |
| | | } |
| | | |
| | | /** Set a property's initial values. */ |
| | | private <T> void setProperty(ManagedObject<?> mo, MyPropertyProvider provider, PropertyDefinition<T> pd) { |
| | | Collection<T> values = provider.getPropertyValues(pd); |
| | | |
| | | // This cannot fail because the property values have already been |
| | | // validated. |
| | | mo.setPropertyValues(pd, values); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument (the one that the user should provide in the command-line) that is equivalent to the |
| | | * modification proposed by the user in the provided PropertyEditorModification object. |
| | | * |
| | | * @param mod |
| | | * the object describing the modification made. |
| | | * @return the argument representing the modification. |
| | | * @throws ArgumentException |
| | | * if there is a problem creating the argument. |
| | | */ |
| | | private static <T> Argument createArgument(PropertyEditorModification<T> mod) throws ArgumentException { |
| | | StringArgument arg; |
| | | |
| | | PropertyDefinition<T> propertyDefinition = mod.getPropertyDefinition(); |
| | | String propName = propertyDefinition.getName(); |
| | | |
| | | switch (mod.getType()) { |
| | | case ADD: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, |
| | | true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | break; |
| | | case SET: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, |
| | | true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | break; |
| | | case RESET: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_RESET, null, OPTION_DSCFG_LONG_RESET, false, true, true, |
| | | INFO_PROPERTY_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_RESET_PROP.get()); |
| | | arg.addValue(propName); |
| | | break; |
| | | case REMOVE: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_REMOVE, null, OPTION_DSCFG_LONG_REMOVE, false, true, true, |
| | | INFO_VALUE_SET_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | arg = null; |
| | | break; |
| | | default: |
| | | // Bug |
| | | throw new IllegalStateException("Unknown modification type: " + mod.getType()); |
| | | } |
| | | return arg; |
| | | } |
| | | |
| | | /** |
| | | * Returns the type name for a given ManagedObjectDefinition. |
| | | * |
| | | * @param d |
| | | * the ManagedObjectDefinition. |
| | | * @return the type name for the provided ManagedObjectDefinition. |
| | | */ |
| | | private String getTypeName(ManagedObjectDefinition<? extends C, ? extends S> d) { |
| | | String name = d.getName(); |
| | | for (String key : types.keySet()) { |
| | | ManagedObjectDefinition<? extends C, ? extends S> current = types.get(key); |
| | | if (current.equals(d)) { |
| | | name = key; |
| | | break; |
| | | } |
| | | } |
| | | return name; |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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-2014 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.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.Utils.SHELL_COMMENT_SEPARATOR; |
| | | import static com.forgerock.opendj.cli.Utils.canWrite; |
| | | import static com.forgerock.opendj.cli.Utils.filterExitCode; |
| | | import static com.forgerock.opendj.cli.Utils.formatDateTimeStringForEquivalentCommand; |
| | | import static com.forgerock.opendj.cli.Utils.getCurrentOperationDateMessage; |
| | | import static com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayManagedObjectDecodingException; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayMissingMandatoryPropertyException; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayOperationRejectedException; |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | |
| | | 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.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.io.PrintStream; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | 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.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.config.ConfigurationFramework; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.Tag; |
| | | 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 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; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class provides a command-line tool which enables |
| | | * administrators to configure the Directory Server. |
| | | */ |
| | | public final class DSConfig extends ConsoleApplication { |
| | | |
| | | /** The logger */ |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** The name of this tool. */ |
| | | final static String DSCONFIGTOOLNAME = "dsconfig"; |
| | | |
| | | /** |
| | | * The name of a command-line script used to launch an administrative tool. |
| | | */ |
| | | final static String PROPERTY_SCRIPT_NAME = "org.opends.server.scriptName"; |
| | | |
| | | /** |
| | | * A menu call-back which runs a sub-command interactively. |
| | | */ |
| | | private class SubCommandHandlerMenuCallback implements MenuCallback<Integer> { |
| | | |
| | | /** 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<Integer> invoke(ConsoleApplication app) |
| | | throws ClientException { |
| | | try { |
| | | MenuResult<Integer> result = handler.run(app, factory); |
| | | |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else { |
| | | if (result.isSuccess() && isInteractive() && |
| | | handler.isCommandBuilderUseful()) |
| | | { |
| | | printCommandBuilder(getCommandBuilder(handler)); |
| | | } |
| | | // Success or cancel. |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.again(); |
| | | } |
| | | } catch (ArgumentException e) { |
| | | app.println(e.getMessageObject()); |
| | | return MenuResult.success(1); |
| | | } catch (ClientException e) { |
| | | app.println(e.getMessageObject()); |
| | | return MenuResult.success(e.getReturnCode()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** The interactive mode sub-menu implementation. */ |
| | | private class SubMenuCallback implements MenuCallback<Integer> { |
| | | |
| | | /** The menu. */ |
| | | private final Menu<Integer> 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<Integer> builder = new MenuBuilder<Integer>(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); |
| | | if (userFriendlyPluralName != null) { |
| | | builder.addNumberedOption( |
| | | INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_PLURAL |
| | | .get(userFriendlyPluralName), callback); |
| | | } else { |
| | | builder |
| | | .addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_SINGULAR |
| | | .get(userFriendlyName), 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); |
| | | if (userFriendlyPluralName != null) { |
| | | builder.addNumberedOption( |
| | | INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_PLURAL |
| | | .get(userFriendlyName), callback); |
| | | } else { |
| | | builder.addNumberedOption( |
| | | INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_SINGULAR |
| | | .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<Integer> invoke(ConsoleApplication app) |
| | | throws ClientException { |
| | | try { |
| | | app.println(); |
| | | app.println(); |
| | | |
| | | MenuResult<Integer> result = menu.run(); |
| | | |
| | | if (result.isCancel()) { |
| | | return MenuResult.again(); |
| | | } |
| | | return result; |
| | | } catch (ClientException e) { |
| | | app.println(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"; |
| | | |
| | | private long sessionStartTime; |
| | | private boolean sessionStartTimePrinted = false; |
| | | private int sessionEquivalentOperationNumber = 0; |
| | | |
| | | /** |
| | | * 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(System.in, outStream, errStream); |
| | | app.sessionStartTime = System.currentTimeMillis(); |
| | | /* |
| | | * FIXME: obtain path info from system properties. |
| | | */ |
| | | if (!ConfigurationFramework.getInstance().isInitialized()) |
| | | { |
| | | try |
| | | { |
| | | ConfigurationFramework.getInstance().initialize(); |
| | | } |
| | | catch (ConfigException e) |
| | | { |
| | | app.println(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 = null; |
| | | |
| | | /** |
| | | * Flag indicating whether or not the global arguments have already been |
| | | * initialized. |
| | | */ |
| | | private boolean globalArgumentsInitialized = false; |
| | | |
| | | /** The sub-command handler factory. */ |
| | | private SubCommandHandlerFactory handlerFactory = null; |
| | | |
| | | /** Mapping of sub-commands to their implementations. */ |
| | | private final Map<SubCommand, SubCommandHandler> handlers = |
| | | new HashMap<SubCommand, SubCommandHandler>(); |
| | | |
| | | /** 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 in |
| | | * The application input stream. |
| | | * @param out |
| | | * The application output stream. |
| | | * @param err |
| | | * The application error stream. |
| | | */ |
| | | private DSConfig(InputStream in, OutputStream out, OutputStream err) { |
| | | super(new PrintStream(out), new PrintStream(err)); |
| | | |
| | | this.parser = new SubCommandArgumentParser(getClass().getName(), |
| | | INFO_DSCFG_TOOL_DESCRIPTION.get(), false); |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@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 message followed by a help usage reference. */ |
| | | private void displayMessageAndUsageReference(LocalizableMessage message) { |
| | | println(message); |
| | | println(); |
| | | println(parser.getHelpUsageReference()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Registers the global arguments with the argument parser. |
| | | * |
| | | * @throws ArgumentException |
| | | * If a global argument could not be registered. |
| | | */ |
| | | private void initializeGlobalArguments(String[] args) |
| | | 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<SubCommand> c = new Comparator<SubCommand>() { |
| | | |
| | | @Override |
| | | public int compare(SubCommand o1, SubCommand o2) { |
| | | return o1.getName().compareTo(o2.getName()); |
| | | } |
| | | }; |
| | | |
| | | Map<Tag, SortedSet<SubCommand>> groups = |
| | | new TreeMap<Tag, SortedSet<SubCommand>>(); |
| | | SortedSet<SubCommand> allSubCommands = new TreeSet<SubCommand>(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<SubCommand> group = groups.get(tag); |
| | | if (group == null) { |
| | | group = new TreeSet<SubCommand>(c); |
| | | groups.put(tag, group); |
| | | } |
| | | group.add(sc); |
| | | } |
| | | } |
| | | |
| | | // Register the usage arguments. |
| | | for (Map.Entry<Tag, SortedSet<SubCommand>> group : groups.entrySet()) { |
| | | Tag tag = group.getKey(); |
| | | SortedSet<SubCommand> 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(args); |
| | | initializeSubCommands(); |
| | | } catch (ArgumentException e) { |
| | | println(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, null); |
| | | cfp.setIsAnAdminConnection(); |
| | | |
| | | // Parse the command-line arguments provided to this program. |
| | | parser.parseArguments(args); |
| | | checkForConflictingArguments(); |
| | | } |
| | | catch (ArgumentException ae) |
| | | { |
| | | displayMessageAndUsageReference(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)) |
| | | { |
| | | println(ERR_DSCFG_CANNOT_WRITE_EQUIVALENT_COMMAND_LINE_FILE.get(file)); |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } |
| | | else |
| | | { |
| | | if (new File(file).isDirectory()) |
| | | { |
| | | println(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) |
| | | { |
| | | displayMessageAndUsageReference(ERR_ERROR_PARSING_ARGS |
| | | .get(e.getMessage())); |
| | | return ReturnCode.CONFLICTING_ARGS.get(); |
| | | } |
| | | |
| | | // Checks the version - if upgrade required, the tool is unusable |
| | | /*try |
| | | { |
| | | BuildVersion.checkVersionMismatch(); |
| | | } |
| | | catch (ConfigException e) |
| | | { |
| | | println(e.getMessageObject()); |
| | | return ReturnCode.ERROR_USER_DATA.get(); |
| | | }*/ |
| | | |
| | | // Handle batch file if any |
| | | if (batchFileArgument.isPresent()) { |
| | | handleBatchFile(args); |
| | | // don't need to do anything else |
| | | return ReturnCode.SUCCESS.get(); |
| | | } |
| | | |
| | | int retCode = 0; |
| | | if (parser.getSubCommand() == null) { |
| | | hasSubCommand = false; |
| | | |
| | | if (isInteractive()) { |
| | | // Top-level interactive mode. |
| | | retCode = runInteractiveMode(); |
| | | } else { |
| | | LocalizableMessage message = ERR_ERROR_PARSING_ARGS |
| | | .get(ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get()); |
| | | displayMessageAndUsageReference(message); |
| | | retCode = ReturnCode.ERROR_USER_DATA.get(); |
| | | } |
| | | } else { |
| | | hasSubCommand = true; |
| | | |
| | | // Retrieve the sub-command implementation and run it. |
| | | SubCommandHandler handler = handlers.get(parser.getSubCommand()); |
| | | retCode = runSubCommand(handler); |
| | | } |
| | | |
| | | factory.close(); |
| | | |
| | | return retCode; |
| | | } |
| | | |
| | | private void checkForConflictingArguments() throws ArgumentException |
| | | { |
| | | if (quietArgument.isPresent() && verboseArgument.isPresent()) { |
| | | final LocalizableMessage message = ERR_TOOL_CONFLICTING_ARGS.get(quietArgument |
| | | .getLongIdentifier(), verboseArgument.getLongIdentifier()); |
| | | throw new ArgumentException(message); |
| | | } |
| | | |
| | | if (batchFileArgument.isPresent() && !noPromptArgument.isPresent()) { |
| | | final LocalizableMessage message = |
| | | ERR_DSCFG_ERROR_QUIET_AND_INTERACTIVE_INCOMPATIBLE.get( |
| | | batchFileArgument.getLongIdentifier(), noPromptArgument |
| | | .getLongIdentifier()); |
| | | throw new ArgumentException(message); |
| | | } |
| | | |
| | | if (quietArgument.isPresent() && !noPromptArgument.isPresent()) { |
| | | final LocalizableMessage message = ERR_DSCFG_ERROR_QUIET_AND_INTERACTIVE_INCOMPATIBLE.get( |
| | | quietArgument.getLongIdentifier(), noPromptArgument |
| | | .getLongIdentifier()); |
| | | throw new ArgumentException(message); |
| | | } |
| | | |
| | | if (scriptFriendlyArgument.isPresent() && verboseArgument.isPresent()) { |
| | | final LocalizableMessage message = ERR_TOOL_CONFLICTING_ARGS.get(scriptFriendlyArgument |
| | | .getLongIdentifier(), verboseArgument.getLongIdentifier()); |
| | | throw new ArgumentException(message); |
| | | } |
| | | |
| | | if (noPropertiesFileArgument.isPresent() |
| | | && propertiesFileArgument.isPresent()) |
| | | { |
| | | final LocalizableMessage message = ERR_TOOL_CONFLICTING_ARGS.get( |
| | | noPropertiesFileArgument.getLongIdentifier(), |
| | | propertiesFileArgument.getLongIdentifier()); |
| | | throw new ArgumentException(message); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** Run the top-level interactive console. */ |
| | | private int runInteractiveMode() { |
| | | |
| | | ConsoleApplication app = this; |
| | | |
| | | // Build menu structure. |
| | | final Comparator<RelationDefinition<?, ?>> c = |
| | | new Comparator<RelationDefinition<?, ?>>() { |
| | | |
| | | @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<RelationDefinition<?, ?>> relations = |
| | | new TreeSet<RelationDefinition<?, ?>>(c); |
| | | final Map<RelationDefinition<?, ?>, CreateSubCommandHandler<?, ?>> createHandlers = |
| | | new HashMap<RelationDefinition<?, ?>, CreateSubCommandHandler<?, ?>>(); |
| | | final Map<RelationDefinition<?, ?>, DeleteSubCommandHandler> deleteHandlers = |
| | | new HashMap<RelationDefinition<?, ?>, DeleteSubCommandHandler>(); |
| | | final Map<RelationDefinition<?, ?>, ListSubCommandHandler> listHandlers = |
| | | new HashMap<RelationDefinition<?, ?>, ListSubCommandHandler>(); |
| | | final Map<RelationDefinition<?, ?>, GetPropSubCommandHandler> getPropHandlers = |
| | | new HashMap<RelationDefinition<?, ?>, GetPropSubCommandHandler>(); |
| | | final Map<RelationDefinition<?, ?>, SetPropSubCommandHandler> setPropHandlers = |
| | | new HashMap<RelationDefinition<?, ?>, 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<Integer> builder = new MenuBuilder<Integer>(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<Integer> 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<Integer> menu = builder.toMenu(); |
| | | |
| | | try { |
| | | // Force retrieval of management context. |
| | | factory.getManagementContext(app); |
| | | } catch (ArgumentException e) { |
| | | app.println(e.getMessageObject()); |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } catch (ClientException e) { |
| | | app.println(e.getMessageObject()); |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } |
| | | |
| | | try { |
| | | app.println(); |
| | | app.println(); |
| | | |
| | | final MenuResult<Integer> result = menu.run(); |
| | | |
| | | if (result.isQuit()) { |
| | | return ReturnCode.SUCCESS.get(); |
| | | } else { |
| | | return result.getValue(); |
| | | } |
| | | } catch (ClientException e) { |
| | | app.println(e.getMessageObject()); |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** Run the provided sub-command handler. */ |
| | | private int runSubCommand(SubCommandHandler handler) { |
| | | try { |
| | | final MenuResult<Integer> 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) { |
| | | println(e.getMessageObject()); |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } catch (ClientException e) { |
| | | Throwable cause = e.getCause(); |
| | | println(); |
| | | 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. |
| | | println(e.getMessageObject()); |
| | | } |
| | | println(); |
| | | |
| | | return ReturnCode.ERROR_UNEXPECTED.get(); |
| | | } catch (Exception e) { |
| | | println(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 <T> |
| | | * SubCommand type. |
| | | * @param subCommand |
| | | * The sub command handler or common. |
| | | * @return <T> The builded command. |
| | | */ |
| | | <T> CommandBuilder getCommandBuilder(final T subCommand) |
| | | { |
| | | String commandName = System.getProperty(PROPERTY_SCRIPT_NAME); |
| | | if (commandName == null) |
| | | { |
| | | commandName = DSCONFIGTOOLNAME; |
| | | } |
| | | CommandBuilder commandBuilder = null; |
| | | if (subCommand instanceof SubCommandHandler) |
| | | { |
| | | commandBuilder = |
| | | new CommandBuilder(commandName, ((SubCommandHandler) subCommand) |
| | | .getSubCommand().getName()); |
| | | } |
| | | else |
| | | { |
| | | commandBuilder = new CommandBuilder(commandName, (String) subCommand); |
| | | } |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | | * 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) |
| | | { |
| | | println(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() |
| | | { |
| | | String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); |
| | | if (scriptName == null || scriptName.length() == 0) |
| | | { |
| | | scriptName = DSCONFIGTOOLNAME; |
| | | } |
| | | final String date = formatDateTimeStringForEquivalentCommand( |
| | | new Date(sessionStartTime)); |
| | | return INFO_DSCFG_SESSION_START_TIME_MESSAGE.get(scriptName, date). |
| | | toString(); |
| | | } |
| | | |
| | | private void handleBatchFile(String[] args) { |
| | | |
| | | BufferedReader bReader = null; |
| | | try { |
| | | // Build a list of initial arguments, |
| | | // removing the batch file option + its value |
| | | final List<String> initialArgs = new ArrayList<String>(); |
| | | Collections.addAll(initialArgs, args); |
| | | int batchFileArgIndex = -1; |
| | | for (final String elem : initialArgs) { |
| | | if (elem.startsWith("-" + OPTION_SHORT_BATCH_FILE_PATH) |
| | | || elem.contains(OPTION_LONG_BATCH_FILE_PATH)) { |
| | | batchFileArgIndex = initialArgs.indexOf(elem); |
| | | break; |
| | | } |
| | | } |
| | | if (batchFileArgIndex != -1) { |
| | | // Remove both the batch file arg and its value |
| | | initialArgs.remove(batchFileArgIndex); |
| | | initialArgs.remove(batchFileArgIndex); |
| | | } |
| | | final String batchFilePath = batchFileArgument.getValue().trim(); |
| | | bReader = |
| | | new BufferedReader(new FileReader(batchFilePath)); |
| | | String line; |
| | | String command = ""; |
| | | // Split the CLI string into arguments array |
| | | 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); |
| | | String displayCommand = new String(command); |
| | | |
| | | // "\ " support |
| | | command = command.replace("\\ ", "##"); |
| | | displayCommand = displayCommand.replace("\\ ", " "); |
| | | |
| | | String[] fileArguments = command.split("\\s+"); |
| | | // reset command |
| | | command = ""; |
| | | for (int ii = 0; ii < fileArguments.length; ii++) { |
| | | fileArguments[ii] = fileArguments[ii].replace("##", " "); |
| | | } |
| | | |
| | | errPrintln(LocalizableMessage.raw(displayCommand)); |
| | | |
| | | // Append initial arguments to the file line |
| | | final List<String> allArguments = new ArrayList<String>(); |
| | | Collections.addAll(allArguments, fileArguments); |
| | | allArguments.addAll(initialArgs); |
| | | final String[] allArgsArray = allArguments.toArray(new String[]{}); |
| | | |
| | | int exitCode = main(allArgsArray, getOutputStream(), getErrorStream()); |
| | | if (exitCode != ReturnCode.SUCCESS.get()) |
| | | { |
| | | System.exit(filterExitCode(exitCode)); |
| | | } |
| | | errPrintln(); |
| | | } |
| | | } catch (IOException ex) { |
| | | println(ERR_DSCFG_ERROR_READING_BATCH_FILE.get(ex)); |
| | | } finally { |
| | | closeSilently(bReader); |
| | | } |
| | | } |
| | | |
| | | /** Replace spaces in quotes by "\ ". */ |
| | | private String replaceSpacesInQuotes(final String line) { |
| | | String newLine = ""; |
| | | 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 += "\\ "; |
| | | } else { |
| | | newLine += ch; |
| | | } |
| | | } |
| | | return newLine; |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.SortedMap; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.config.client.OperationRejectedException; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.BooleanArgument; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | 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.TableBuilder; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to delete existing managed objects. |
| | | * <p> |
| | | * This sub-command implements the various delete-xxx sub-commands. |
| | | */ |
| | | final class DeleteSubCommandHandler extends SubCommandHandler { |
| | | |
| | | /** |
| | | * The value for the long option force. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_FORCE = "force"; |
| | | |
| | | /** |
| | | * The value for the short option force. |
| | | */ |
| | | private static final char OPTION_DSCFG_SHORT_FORCE = 'f'; |
| | | |
| | | /** |
| | | * Creates a new delete-xxx sub-command for an instantiable relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The instantiable relation. |
| | | * @return Returns the new delete-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static DeleteSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | InstantiableRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new DeleteSubCommandHandler(parser, p, r, p.child(r, "DUMMY")); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new delete-xxx sub-command for an optional relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The optional relation. |
| | | * @return Returns the new delete-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static DeleteSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | OptionalRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new DeleteSubCommandHandler(parser, p, r, p.child(r)); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new delete-xxx sub-command for a set relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The set relation. |
| | | * @return Returns the new delete-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static DeleteSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | SetRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new DeleteSubCommandHandler(parser, p, r, p.child(r)); |
| | | } |
| | | |
| | | /** The argument which should be used to force deletion. */ |
| | | private final BooleanArgument forceArgument; |
| | | |
| | | /** The sub-commands naming arguments. */ |
| | | private final List<StringArgument> namingArgs; |
| | | |
| | | /** The path of the managed object. */ |
| | | private final ManagedObjectPath<?, ?> path; |
| | | |
| | | /** |
| | | * The relation which references the managed object to be deleted. |
| | | */ |
| | | private final RelationDefinition<?, ?> relation; |
| | | |
| | | /** The sub-command associated with this handler. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** Private constructor. */ |
| | | private DeleteSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | RelationDefinition<?, ?> r, ManagedObjectPath<?, ?> c) throws ArgumentException { |
| | | this.path = p; |
| | | this.relation = r; |
| | | |
| | | // Create the sub-command. |
| | | String name = "delete-" + r.getName(); |
| | | LocalizableMessage ufpn = r.getChildDefinition().getUserFriendlyPluralName(); |
| | | LocalizableMessage description = INFO_DSCFG_DESCRIPTION_SUBCMD_DELETE.get(ufpn); |
| | | this.subCommand = new SubCommand(parser, name, false, 0, 0, null, description); |
| | | |
| | | // Create the naming arguments. |
| | | this.namingArgs = createNamingArgs(subCommand, c, false); |
| | | |
| | | // Create the --force argument which is used to force deletion. |
| | | this.forceArgument = new BooleanArgument(OPTION_DSCFG_LONG_FORCE, OPTION_DSCFG_SHORT_FORCE, |
| | | OPTION_DSCFG_LONG_FORCE, INFO_DSCFG_DESCRIPTION_FORCE.get(ufpn)); |
| | | subCommand.addArgument(forceArgument); |
| | | |
| | | // Register the tags associated with the child managed objects. |
| | | addTags(relation.getChildDefinition().getAllTags()); |
| | | } |
| | | |
| | | /** |
| | | * Gets the relation definition associated with the type of component that this sub-command handles. |
| | | * |
| | | * @return Returns the relation definition associated with the type of component that this sub-command handles. |
| | | */ |
| | | public RelationDefinition<?, ?> getRelationDefinition() { |
| | | return relation; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | // Get the naming argument values. |
| | | List<String> names = getNamingArgValues(app, namingArgs); |
| | | |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | setCommandBuilderUseful(false); |
| | | |
| | | // Delete the child managed object. |
| | | ManagementContext context = factory.getManagementContext(app); |
| | | MenuResult<ManagedObject<?>> result; |
| | | LocalizableMessage ufn = relation.getUserFriendlyName(); |
| | | try { |
| | | result = getManagedObject(app, context, path, names); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(pufn, pufn, pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // Ignore the error if the deletion is being forced. |
| | | if (!forceArgument.isPresent()) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(pufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } else { |
| | | return MenuResult.success(0); |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to cancel deletion. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_DELETE_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | // Must be menu driven, so no need for error message. |
| | | return MenuResult.cancel(); |
| | | } |
| | | |
| | | ManagedObject<?> parent = result.getValue(); |
| | | try { |
| | | if (relation instanceof InstantiableRelationDefinition || relation instanceof SetRelationDefinition) { |
| | | String childName = names.get(names.size() - 1); |
| | | |
| | | if (childName == null) { |
| | | MenuResult<String> sresult = readChildName(app, parent, relation, null); |
| | | |
| | | if (sresult.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to cancel deletion. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_DELETE_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (sresult.isCancel()) { |
| | | // Must be menu driven, so no need for error message. |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | childName = sresult.getValue(); |
| | | } |
| | | } else if (relation instanceof SetRelationDefinition) { |
| | | // The provided type short name needs mapping to the full name. |
| | | String name = childName.trim(); |
| | | SortedMap<?, ?> types = getSubTypes(relation.getChildDefinition()); |
| | | ManagedObjectDefinition<?, ?> cd = (ManagedObjectDefinition<?, ?>) types.get(name); |
| | | if (cd == null) { |
| | | // The name must be invalid. |
| | | String typeUsage = getSubTypesUsage(relation.getChildDefinition()); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED.get(name, |
| | | relation.getUserFriendlyName(), typeUsage); |
| | | throw new ArgumentException(msg); |
| | | } else { |
| | | childName = cd.getName(); |
| | | } |
| | | } |
| | | |
| | | if (confirmDeletion(app)) { |
| | | setCommandBuilderUseful(true); |
| | | if (relation instanceof InstantiableRelationDefinition) { |
| | | parent.removeChild((InstantiableRelationDefinition<?, ?>) relation, childName); |
| | | } else { |
| | | parent.removeChild((SetRelationDefinition<?, ?>) relation, childName); |
| | | } |
| | | } else { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } else if (relation instanceof OptionalRelationDefinition) { |
| | | OptionalRelationDefinition<?, ?> orelation = (OptionalRelationDefinition<?, ?>) relation; |
| | | |
| | | if (confirmDeletion(app)) { |
| | | setCommandBuilderUseful(true); |
| | | |
| | | parent.removeChild(orelation); |
| | | } else { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (OperationRejectedException e) { |
| | | LocalizableMessage msg; |
| | | if (e.getMessages().size() == 1) { |
| | | msg = ERR_DSCFG_ERROR_DELETE_ORE_SINGLE.get(ufn); |
| | | } else { |
| | | msg = ERR_DSCFG_ERROR_DELETE_ORE_PLURAL.get(ufn); |
| | | } |
| | | |
| | | if (app.isInteractive()) { |
| | | // If interactive, let the user go back to the main menu. |
| | | app.println(); |
| | | app.println(msg); |
| | | app.println(); |
| | | TableBuilder builder = new TableBuilder(); |
| | | for (LocalizableMessage reason : e.getMessages()) { |
| | | builder.startRow(); |
| | | builder.appendCell("*"); |
| | | builder.appendCell(reason); |
| | | } |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setColumnWidth(1, 0); |
| | | printer.setIndentWidth(4); |
| | | builder.print(printer); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg, e); |
| | | } |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // Ignore the error if the deletion is being forced. |
| | | if (!forceArgument.isPresent()) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_MONFE.get(ufn); |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_DELETE_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } |
| | | |
| | | // Add the naming arguments if they were provided. |
| | | for (StringArgument arg : namingArgs) { |
| | | if (arg.isPresent()) { |
| | | getCommandBuilder().addArgument(arg); |
| | | } |
| | | } |
| | | |
| | | // Output success message. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_DELETE_SUCCESS.get(ufn)); |
| | | |
| | | return MenuResult.success(0); |
| | | } |
| | | |
| | | /** Confirm deletion. */ |
| | | private boolean confirmDeletion(ConsoleApplication app) throws ClientException { |
| | | if (app.isInteractive()) { |
| | | LocalizableMessage prompt = INFO_DSCFG_CONFIRM_DELETE.get(relation.getUserFriendlyName()); |
| | | app.println(); |
| | | if (!app.confirmAction(prompt, false)) { |
| | | // Output failure message. |
| | | LocalizableMessage msg = INFO_DSCFG_CONFIRM_DELETE_FAIL.get(relation.getUserFriendlyName()); |
| | | app.println(msg); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.LIST_TABLE_SEPARATOR; |
| | | |
| | | import java.io.PrintStream; |
| | | import java.util.Collection; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.SortedSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; |
| | | import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | 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.SingletonRelationDefinition; |
| | | import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | 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.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.MenuResult; |
| | | import com.forgerock.opendj.cli.TableBuilder; |
| | | import com.forgerock.opendj.cli.TablePrinter; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to retrieve the properties of a managed object. |
| | | * <p> |
| | | * This sub-command implements the various get-xxx-prop sub-commands. |
| | | */ |
| | | final class GetPropSubCommandHandler extends SubCommandHandler { |
| | | |
| | | /** |
| | | * Creates a new get-xxx-prop sub-command for an instantiable relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The instantiable relation. |
| | | * @return Returns the new get-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static GetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | InstantiableRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new GetPropSubCommandHandler(parser, path.child(r, "DUMMY"), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new get-xxx-prop sub-command for an optional relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The optional relation. |
| | | * @return Returns the new get-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static GetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | OptionalRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new GetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new get-xxx-prop sub-command for a set relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The set relation. |
| | | * @return Returns the new get-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static GetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | SetRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new GetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new get-xxx-prop sub-command for a singleton relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The singleton relation. |
| | | * @return Returns the new get-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static GetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | SingletonRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new GetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | // The sub-commands naming arguments. |
| | | private final List<StringArgument> namingArgs; |
| | | |
| | | // The path of the managed object. |
| | | private final ManagedObjectPath<?, ?> path; |
| | | |
| | | // The sub-command associated with this handler. |
| | | private final SubCommand subCommand; |
| | | |
| | | // Private constructor. |
| | | private GetPropSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | RelationDefinition<?, ?> r) throws ArgumentException { |
| | | this.path = path; |
| | | |
| | | // Create the sub-command. |
| | | String name = "get-" + r.getName() + "-prop"; |
| | | LocalizableMessage message = INFO_DSCFG_DESCRIPTION_SUBCMD_GETPROP.get(r.getChildDefinition() |
| | | .getUserFriendlyName()); |
| | | this.subCommand = new SubCommand(parser, name, false, 0, 0, null, message); |
| | | |
| | | // Create the naming arguments. |
| | | this.namingArgs = createNamingArgs(subCommand, path, false); |
| | | |
| | | // Register common arguments. |
| | | registerPropertyNameArgument(this.subCommand); |
| | | registerRecordModeArgument(this.subCommand); |
| | | registerUnitSizeArgument(this.subCommand); |
| | | registerUnitTimeArgument(this.subCommand); |
| | | |
| | | // Register the tags associated with the child managed objects. |
| | | addTags(path.getManagedObjectDefinition().getAllTags()); |
| | | } |
| | | |
| | | /** |
| | | * Gets the relation definition associated with the type of component that this sub-command handles. |
| | | * |
| | | * @return Returns the relation definition associated with the type of component that this sub-command handles. |
| | | */ |
| | | public RelationDefinition<?, ?> getRelationDefinition() { |
| | | return path.getRelationDefinition(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | // Get the property names. |
| | | Set<String> propertyNames = getPropertyNames(); |
| | | PropertyValuePrinter valuePrinter = new PropertyValuePrinter(getSizeUnit(), getTimeUnit(), |
| | | app.isScriptFriendly()); |
| | | |
| | | // Get the naming argument values. |
| | | List<String> names = getNamingArgValues(app, namingArgs); |
| | | |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | |
| | | setCommandBuilderUseful(false); |
| | | |
| | | // Update the command builder. |
| | | updateCommandBuilderWithSubCommand(); |
| | | |
| | | // Get the targeted managed object. |
| | | LocalizableMessage ufn = path.getRelationDefinition().getUserFriendlyName(); |
| | | ManagementContext context = factory.getManagementContext(app); |
| | | MenuResult<ManagedObject<?>> result; |
| | | try { |
| | | result = getManagedObject(app, context, path, names); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(ufn); |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | |
| | | // Validate the property names. |
| | | ManagedObject<?> child = result.getValue(); |
| | | ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition(); |
| | | Collection<PropertyDefinition<?>> pdList; |
| | | if (propertyNames.isEmpty()) { |
| | | pdList = d.getAllPropertyDefinitions(); |
| | | } else { |
| | | pdList = new LinkedList<PropertyDefinition<?>>(); |
| | | for (String name : propertyNames) { |
| | | try { |
| | | pdList.add(d.getPropertyDefinition(name)); |
| | | } catch (IllegalArgumentException e) { |
| | | throw ArgumentExceptionFactory.unknownProperty(d, name); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Now output its properties. |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_VALUE.get()); |
| | | builder.addSortKey(0); |
| | | for (PropertyDefinition<?> pd : pdList) { |
| | | if (pd.hasOption(PropertyOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | if (propertyNames.isEmpty() || propertyNames.contains(pd.getName())) { |
| | | displayProperty(app, builder, child, pd, valuePrinter); |
| | | setCommandBuilderUseful(true); |
| | | } |
| | | } |
| | | |
| | | PrintStream out = app.getOutputStream(); |
| | | if (app.isScriptFriendly()) { |
| | | TablePrinter printer = createScriptFriendlyTablePrinter(out); |
| | | builder.print(printer); |
| | | } else { |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | printer.setColumnSeparator(LIST_TABLE_SEPARATOR); |
| | | printer.setColumnWidth(1, 0); |
| | | builder.print(printer); |
| | | } |
| | | |
| | | return MenuResult.success(0); |
| | | } |
| | | |
| | | // Display the set of values associated with a property. |
| | | private <T> void displayProperty(final ConsoleApplication app, TableBuilder builder, ManagedObject<?> mo, |
| | | PropertyDefinition<T> pd, PropertyValuePrinter valuePrinter) { |
| | | SortedSet<T> values = mo.getPropertyValues(pd); |
| | | if (values.isEmpty()) { |
| | | // There are no values or default values. Display the default |
| | | // behavior for alias values. |
| | | DefaultBehaviorProviderVisitor<T, LocalizableMessage, Void> visitor |
| | | = new DefaultBehaviorProviderVisitor<T, LocalizableMessage, Void>() { |
| | | |
| | | public LocalizableMessage visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, |
| | | Void p) { |
| | | // Should not happen - inherited default values are |
| | | // displayed as normal values. |
| | | throw new IllegalStateException(); |
| | | } |
| | | |
| | | public LocalizableMessage visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) { |
| | | if (app.isVerbose()) { |
| | | return d.getSynopsis(); |
| | | } else { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public LocalizableMessage visitDefined(DefinedDefaultBehaviorProvider<T> d, Void p) { |
| | | // Should not happen - real default values are displayed as |
| | | // normal values. |
| | | throw new IllegalStateException(); |
| | | } |
| | | |
| | | public LocalizableMessage visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, |
| | | Void p) { |
| | | // Should not happen - inherited default values are |
| | | // displayed as normal values. |
| | | throw new IllegalStateException(); |
| | | } |
| | | |
| | | public LocalizableMessage visitUndefined(UndefinedDefaultBehaviorProvider<T> d, Void p) { |
| | | return null; |
| | | } |
| | | }; |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(pd.getName()); |
| | | |
| | | LocalizableMessage content = pd.getDefaultBehaviorProvider().accept(visitor, null); |
| | | if (content == null) { |
| | | if (app.isScriptFriendly()) { |
| | | builder.appendCell(); |
| | | } else { |
| | | builder.appendCell("-"); |
| | | } |
| | | } else { |
| | | builder.appendCell(content); |
| | | } |
| | | } else { |
| | | if (isRecordMode()) { |
| | | for (T value : values) { |
| | | builder.startRow(); |
| | | builder.appendCell(pd.getName()); |
| | | builder.appendCell(valuePrinter.print(pd, value)); |
| | | } |
| | | } else { |
| | | builder.startRow(); |
| | | builder.appendCell(pd.getName()); |
| | | |
| | | if (app.isScriptFriendly()) { |
| | | for (T value : values) { |
| | | builder.appendCell(valuePrinter.print(pd, value)); |
| | | } |
| | | } else { |
| | | StringBuilder sb = new StringBuilder(); |
| | | boolean isFirst = true; |
| | | for (T value : values) { |
| | | if (!isFirst) { |
| | | sb.append(", "); |
| | | } |
| | | sb.append(valuePrinter.print(pd, value)); |
| | | isFirst = false; |
| | | } |
| | | |
| | | builder.appendCell(sb.toString()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static com.forgerock.opendj.cli.Utils.MAX_LINE_WIDTH; |
| | | |
| | | import java.io.PrintStream; |
| | | import java.util.Collection; |
| | | import java.util.EnumSet; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.TreeMap; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.AdministratorAction; |
| | | import org.forgerock.opendj.config.AggregationPropertyDefinition; |
| | | import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; |
| | | import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.EnumPropertyDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectOption; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; |
| | | import org.forgerock.opendj.config.PropertyDefinitionVisitor; |
| | | import org.forgerock.opendj.config.PropertyOption; |
| | | import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.StringPropertyDefinition; |
| | | import org.forgerock.opendj.config.Tag; |
| | | import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.BooleanArgument; |
| | | import com.forgerock.opendj.cli.StringArgument; |
| | | import com.forgerock.opendj.cli.SubCommand; |
| | | import com.forgerock.opendj.cli.SubCommandArgumentParser; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.MenuResult; |
| | | import com.forgerock.opendj.cli.TableBuilder; |
| | | import com.forgerock.opendj.cli.TablePrinter; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to display help about managed objects and their properties. |
| | | * <p> |
| | | * This sub-command implements the help-properties sub-command. |
| | | */ |
| | | final class HelpSubCommandHandler extends SubCommandHandler { |
| | | |
| | | /** |
| | | * This class is used to print the default behavior of a property. |
| | | */ |
| | | private static class DefaultBehaviorPrinter { |
| | | |
| | | /** |
| | | * The default behavior printer visitor implementation. |
| | | * |
| | | * @param <T> |
| | | * The property type. |
| | | */ |
| | | private static class DefaultVisitor<T> implements |
| | | DefaultBehaviorProviderVisitor<T, LocalizableMessage, PropertyDefinition<T>> { |
| | | |
| | | /** {@inheritDoc} */ |
| | | public LocalizableMessage visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | return INFO_DSCFG_HELP_FIELD_INHERITED_ABS.get(d.getPropertyName(), d.getManagedObjectPath() |
| | | .getRelationDefinition().getUserFriendlyName()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public LocalizableMessage visitAlias(AliasDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { |
| | | return d.getSynopsis(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public LocalizableMessage visitDefined(DefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { |
| | | LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); |
| | | PropertyValuePrinter printer = new PropertyValuePrinter(null, null, false); |
| | | boolean isFirst = true; |
| | | for (String s : d.getDefaultValues()) { |
| | | if (!isFirst) { |
| | | builder.append(", "); |
| | | } |
| | | |
| | | T value = p.decodeValue(s); |
| | | builder.append(printer.print(p, value)); |
| | | } |
| | | |
| | | return builder.toMessage(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public LocalizableMessage visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | if (d.getRelativeOffset() == 0) { |
| | | return INFO_DSCFG_HELP_FIELD_INHERITED_THIS.get(d.getPropertyName(), d.getManagedObjectDefinition() |
| | | .getUserFriendlyName()); |
| | | } else { |
| | | return INFO_DSCFG_HELP_FIELD_INHERITED_PARENT.get(d.getPropertyName(), d |
| | | .getManagedObjectDefinition().getUserFriendlyName()); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public LocalizableMessage visitUndefined(UndefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { |
| | | return INFO_DSCFG_HELP_FIELD_UNDEFINED.get(); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Create a new default behavior printer. |
| | | */ |
| | | public DefaultBehaviorPrinter() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | /** |
| | | * Get a user-friendly description of a property's default behavior. |
| | | * |
| | | * @param <T> |
| | | * The type of the property definition. |
| | | * @param pd |
| | | * The property definition. |
| | | * @return Returns the user-friendly description of a property's default behavior. |
| | | */ |
| | | public <T> LocalizableMessage print(PropertyDefinition<T> pd) { |
| | | DefaultVisitor<T> v = new DefaultVisitor<T>(); |
| | | return pd.getDefaultBehaviorProvider().accept(v, pd); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This class is used to print detailed syntax information about a property. |
| | | */ |
| | | private static class SyntaxPrinter { |
| | | |
| | | /** |
| | | * The syntax printer visitor implementation. |
| | | */ |
| | | private static final class Visitor extends PropertyDefinitionVisitor<Void, PrintStream> { |
| | | |
| | | /** Private constructor. */ |
| | | private Visitor() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <E extends Enum<E>> Void visitEnum(EnumPropertyDefinition<E> d, PrintStream p) { |
| | | displayUsage(p, INFO_DSCFG_HELP_FIELD_ENUM.get()); |
| | | p.println(); |
| | | |
| | | TableBuilder builder = new TableBuilder(); |
| | | boolean isFirst = true; |
| | | for (E value : EnumSet.<E> allOf(d.getEnumClass())) { |
| | | if (!isFirst) { |
| | | builder.startRow(); |
| | | } |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(); |
| | | builder.appendCell(); |
| | | builder.appendCell(value.toString()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | builder.appendCell(d.getValueSynopsis(value)); |
| | | |
| | | isFirst = false; |
| | | } |
| | | |
| | | TextTablePrinter factory = new TextTablePrinter(p); |
| | | factory.setDisplayHeadings(false); |
| | | factory.setColumnWidth(0, HEADING_WIDTH); |
| | | factory.setColumnWidth(1, HEADING_SEPARATOR.length()); |
| | | factory.setColumnWidth(4, 0); |
| | | factory.setPadding(0); |
| | | |
| | | builder.print(factory); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Void visitString(StringPropertyDefinition d, PrintStream p) { |
| | | PropertyDefinitionUsageBuilder usageBuilder = new PropertyDefinitionUsageBuilder(false); |
| | | |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_SYNTAX.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | builder.appendCell(usageBuilder.getUsage(d)); |
| | | |
| | | if (d.getPattern() != null) { |
| | | builder.startRow(); |
| | | builder.startRow(); |
| | | builder.appendCell(); |
| | | builder.appendCell(); |
| | | builder.appendCell(d.getPatternSynopsis()); |
| | | } |
| | | |
| | | TextTablePrinter factory = new TextTablePrinter(p); |
| | | factory.setDisplayHeadings(false); |
| | | factory.setColumnWidth(0, HEADING_WIDTH); |
| | | factory.setColumnWidth(2, 0); |
| | | factory.setPadding(0); |
| | | |
| | | builder.print(factory); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> Void visitUnknown(PropertyDefinition<T> d, PrintStream p) { |
| | | PropertyDefinitionUsageBuilder usageBuilder = new PropertyDefinitionUsageBuilder(true); |
| | | displayUsage(p, usageBuilder.getUsage(d)); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** Common usage. */ |
| | | private void displayUsage(PrintStream p, LocalizableMessage usage) { |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_SYNTAX.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | builder.appendCell(usage); |
| | | |
| | | TextTablePrinter factory = new TextTablePrinter(p); |
| | | factory.setDisplayHeadings(false); |
| | | factory.setColumnWidth(0, HEADING_WIDTH); |
| | | factory.setColumnWidth(2, 0); |
| | | factory.setPadding(0); |
| | | |
| | | builder.print(factory); |
| | | } |
| | | } |
| | | |
| | | /** The private implementation. */ |
| | | private final Visitor pimpl; |
| | | |
| | | /** |
| | | * Creates a new syntax printer which can be used to print detailed syntax information about a property. |
| | | */ |
| | | public SyntaxPrinter() { |
| | | this.pimpl = new Visitor(); |
| | | } |
| | | |
| | | /** |
| | | * Print detailed syntax information about a property definition. |
| | | * |
| | | * @param out |
| | | * The output stream. |
| | | * @param pd |
| | | * The property definition. |
| | | */ |
| | | public void print(PrintStream out, PropertyDefinition<?> pd) { |
| | | pd.accept(pimpl, out); |
| | | } |
| | | } |
| | | |
| | | /** Strings used in property help. */ |
| | | private static final String HEADING_SEPARATOR = " : "; |
| | | |
| | | /** Width of biggest heading (need to be careful of I18N). */ |
| | | private static final int HEADING_WIDTH; |
| | | |
| | | /** |
| | | * The value for the long option category. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_CATEGORY = "category"; |
| | | |
| | | /** |
| | | * The value for the long option inherited. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_INHERITED = "inherited"; |
| | | |
| | | /** |
| | | * The value for the long option type. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_TYPE = "type"; |
| | | |
| | | /** |
| | | * The value for the short option category. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_CATEGORY = 'c'; |
| | | |
| | | /** |
| | | * The value for the short option inherited. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_INHERITED = null; |
| | | |
| | | /** |
| | | * The value for the short option type. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_TYPE = 't'; |
| | | |
| | | static { |
| | | int tmp = INFO_DSCFG_HELP_HEADING_SYNTAX.get().length(); |
| | | tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_DEFAULT.get().length()); |
| | | tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_MULTI_VALUED.get().length()); |
| | | tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_MANDATORY.get().length()); |
| | | tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_READ_ONLY.get().length()); |
| | | HEADING_WIDTH = tmp; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new help-properties sub-command. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @return Returns the new help-properties sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static HelpSubCommandHandler create(SubCommandArgumentParser parser) throws ArgumentException { |
| | | return new HelpSubCommandHandler(parser); |
| | | } |
| | | |
| | | /** |
| | | * Displays detailed help about a single component to the specified output stream. |
| | | * |
| | | * @param app |
| | | * The application console. |
| | | * @param mo |
| | | * The managed object. |
| | | * @param c |
| | | * The collection of properties to be displayed. |
| | | */ |
| | | public static void displaySingleComponent(ConsoleApplication app, ManagedObject<?> mo, |
| | | Collection<PropertyDefinition<?>> c) { |
| | | String ufn = mo.getManagedObjectPath().getName(); |
| | | if (ufn == null) { |
| | | ufn = mo.getManagedObjectDefinition().getUserFriendlyName().toString(); |
| | | } |
| | | // Display the title. |
| | | app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(ufn)); |
| | | |
| | | final AbstractManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); |
| | | |
| | | app.println(); |
| | | app.println(d.getSynopsis()); |
| | | if (d.getDescription() != null) { |
| | | app.println(); |
| | | app.println(d.getDescription()); |
| | | } |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | displayPropertyOptionKey(app); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | |
| | | // Headings. |
| | | final TableBuilder builder = new TableBuilder(); |
| | | |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_OPTIONS.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get()); |
| | | |
| | | // Sort keys. |
| | | builder.addSortKey(0); |
| | | |
| | | // Output summary of each property. |
| | | for (final PropertyDefinition<?> pd : c) { |
| | | // Display the property. |
| | | builder.startRow(); |
| | | |
| | | // Display the property name. |
| | | builder.appendCell(pd.getName()); |
| | | |
| | | // Display the options. |
| | | builder.appendCell(getPropertyOptionSummary(pd)); |
| | | |
| | | // Display the syntax. |
| | | final PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(false); |
| | | builder.appendCell(v.getUsage(pd)); |
| | | } |
| | | |
| | | builder.print(new TextTablePrinter(app.getErrorStream())); |
| | | } |
| | | |
| | | /** |
| | | * Displays detailed help about a single property to the specified output stream. |
| | | * |
| | | * @param app |
| | | * The application console. |
| | | * @param d |
| | | * The managed object definition. |
| | | * @param name |
| | | * The name of the property definition. |
| | | */ |
| | | public static void displayVerboseSingleProperty(ConsoleApplication app, AbstractManagedObjectDefinition<?, ?> d, |
| | | String name) { |
| | | PropertyDefinition<?> pd = d.getPropertyDefinition(name); |
| | | |
| | | // Display the title. |
| | | app.println(INFO_DSCFG_HELP_HEADING_PROPERTY.get(name)); |
| | | |
| | | // Display the property synopsis and description. |
| | | app.println(); |
| | | app.errPrintln(pd.getSynopsis(), 4); |
| | | |
| | | if (pd.getDescription() != null) { |
| | | app.println(); |
| | | app.errPrintln(pd.getDescription(), 4); |
| | | } |
| | | |
| | | if (pd instanceof AggregationPropertyDefinition) { |
| | | AggregationPropertyDefinition<?, ?> apd = (AggregationPropertyDefinition<?, ?>) pd; |
| | | if (apd.getSourceConstraintSynopsis() != null) { |
| | | app.println(); |
| | | app.println(apd.getSourceConstraintSynopsis(), 4); |
| | | } |
| | | } |
| | | |
| | | // Display the syntax. |
| | | app.println(); |
| | | SyntaxPrinter syntaxPrinter = new SyntaxPrinter(); |
| | | syntaxPrinter.print(app.getErrorStream(), pd); |
| | | |
| | | // Display remaining information in a table. |
| | | app.println(); |
| | | TableBuilder builder = new TableBuilder(); |
| | | |
| | | // Display the default behavior. |
| | | DefaultBehaviorPrinter defaultPrinter = new DefaultBehaviorPrinter(); |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_DEFAULT.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | builder.appendCell(defaultPrinter.print(pd)); |
| | | |
| | | // Display options. |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_ADVANCED.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | if (pd.hasOption(PropertyOption.ADVANCED)) { |
| | | builder.appendCell(INFO_GENERAL_YES.get()); |
| | | } else { |
| | | builder.appendCell(INFO_GENERAL_NO.get()); |
| | | } |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_MULTI_VALUED.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | if (pd.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | builder.appendCell(INFO_GENERAL_YES.get()); |
| | | } else { |
| | | builder.appendCell(INFO_GENERAL_NO.get()); |
| | | } |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_MANDATORY.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | if (pd.hasOption(PropertyOption.MANDATORY)) { |
| | | builder.appendCell(INFO_GENERAL_YES.get()); |
| | | } else { |
| | | builder.appendCell(INFO_GENERAL_NO.get()); |
| | | } |
| | | |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_DSCFG_HELP_HEADING_READ_ONLY.get()); |
| | | builder.appendCell(HEADING_SEPARATOR); |
| | | if (pd.hasOption(PropertyOption.MONITORING)) { |
| | | builder.appendCell(INFO_DSCFG_HELP_FIELD_MONITORING.get()); |
| | | } else if (pd.hasOption(PropertyOption.READ_ONLY)) { |
| | | builder.appendCell(INFO_DSCFG_HELP_FIELD_READ_ONLY.get(d.getUserFriendlyName())); |
| | | } else { |
| | | builder.appendCell(INFO_GENERAL_NO.get()); |
| | | } |
| | | |
| | | TextTablePrinter factory = new TextTablePrinter(app.getErrorStream()); |
| | | factory.setDisplayHeadings(false); |
| | | factory.setColumnWidth(0, HEADING_WIDTH); |
| | | factory.setColumnWidth(2, 0); |
| | | factory.setPadding(0); |
| | | builder.print(factory); |
| | | |
| | | // Administrator action. |
| | | AdministratorAction action = pd.getAdministratorAction(); |
| | | LocalizableMessage synopsis = action.getSynopsis(); |
| | | if (synopsis == null) { |
| | | switch (action.getType()) { |
| | | case COMPONENT_RESTART: |
| | | synopsis = INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART.get(d.getUserFriendlyName()); |
| | | break; |
| | | case SERVER_RESTART: |
| | | synopsis = INFO_DSCFG_HELP_FIELD_SERVER_RESTART.get(); |
| | | break; |
| | | default: |
| | | // Do nothing. |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (synopsis != null) { |
| | | app.println(); |
| | | app.println(synopsis); |
| | | } |
| | | } |
| | | |
| | | /** Displays the property option summary key. */ |
| | | private static void displayPropertyOptionKey(ConsoleApplication app) { |
| | | LocalizableMessageBuilder builder; |
| | | |
| | | app.println(INFO_DSCFG_HELP_DESCRIPTION_OPTION.get()); |
| | | app.println(); |
| | | |
| | | builder = new LocalizableMessageBuilder(); |
| | | builder.append(" r -- "); |
| | | builder.append(INFO_DSCFG_HELP_DESCRIPTION_READ.get()); |
| | | app.println(builder.toMessage()); |
| | | |
| | | builder = new LocalizableMessageBuilder(); |
| | | builder.append(" w -- "); |
| | | builder.append(INFO_DSCFG_HELP_DESCRIPTION_WRITE.get()); |
| | | app.println(builder.toMessage()); |
| | | |
| | | builder = new LocalizableMessageBuilder(); |
| | | builder.append(" m -- "); |
| | | builder.append(INFO_DSCFG_HELP_DESCRIPTION_MANDATORY.get()); |
| | | app.println(builder.toMessage()); |
| | | |
| | | builder = new LocalizableMessageBuilder(); |
| | | builder.append(" s -- "); |
| | | builder.append(INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED.get()); |
| | | app.println(builder.toMessage()); |
| | | |
| | | builder = new LocalizableMessageBuilder(); |
| | | builder.append(" a -- "); |
| | | builder.append(INFO_DSCFG_HELP_DESCRIPTION_ADMIN_ACTION.get()); |
| | | app.println(builder.toMessage()); |
| | | } |
| | | |
| | | /** Compute the options field. */ |
| | | private static String getPropertyOptionSummary(PropertyDefinition<?> pd) { |
| | | StringBuilder b = new StringBuilder(); |
| | | |
| | | if (pd.hasOption(PropertyOption.MONITORING) || pd.hasOption(PropertyOption.READ_ONLY)) { |
| | | b.append("r-"); |
| | | } else { |
| | | b.append("rw"); |
| | | } |
| | | |
| | | if (pd.hasOption(PropertyOption.MANDATORY)) { |
| | | b.append('m'); |
| | | } else { |
| | | b.append('-'); |
| | | } |
| | | |
| | | if (pd.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | b.append('-'); |
| | | } else { |
| | | b.append('s'); |
| | | } |
| | | |
| | | AdministratorAction action = pd.getAdministratorAction(); |
| | | if (action.getType() != AdministratorAction.Type.NONE) { |
| | | b.append('a'); |
| | | } else { |
| | | b.append('-'); |
| | | } |
| | | return b.toString(); |
| | | } |
| | | |
| | | /** |
| | | * The argument which should be used to specify the category of managed object to be retrieved. |
| | | */ |
| | | private final StringArgument categoryArgument; |
| | | |
| | | /** |
| | | * A table listing all the available types of managed object indexed on their parent type. |
| | | */ |
| | | private final Map<String, Map<String, AbstractManagedObjectDefinition<?, ?>>> categoryMap; |
| | | |
| | | /** |
| | | * The argument which should be used to display inherited properties. |
| | | */ |
| | | private BooleanArgument inheritedModeArgument; |
| | | |
| | | /** The sub-command associated with this handler. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** |
| | | * A table listing all the available types of managed object indexed on their tag(s). |
| | | */ |
| | | private final Map<Tag, Map<String, AbstractManagedObjectDefinition<?, ?>>> tagMap; |
| | | |
| | | /** |
| | | * The argument which should be used to specify the sub-type of managed object to be retrieved. |
| | | */ |
| | | private final StringArgument typeArgument; |
| | | |
| | | /** Private constructor. */ |
| | | private HelpSubCommandHandler(SubCommandArgumentParser parser) throws ArgumentException { |
| | | // Create the sub-command. |
| | | String name = "list-properties"; |
| | | LocalizableMessage desc = INFO_DSCFG_DESCRIPTION_SUBCMD_HELPPROP.get(); |
| | | this.subCommand = new SubCommand(parser, name, false, 0, 0, null, desc); |
| | | |
| | | this.categoryArgument = new StringArgument(OPTION_DSCFG_LONG_CATEGORY, OPTION_DSCFG_SHORT_CATEGORY, |
| | | OPTION_DSCFG_LONG_CATEGORY, false, false, true, INFO_CATEGORY_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_HELP_CATEGORY.get()); |
| | | this.subCommand.addArgument(this.categoryArgument); |
| | | |
| | | this.typeArgument = new StringArgument(OPTION_DSCFG_LONG_TYPE, OPTION_DSCFG_SHORT_TYPE, OPTION_DSCFG_LONG_TYPE, |
| | | false, false, true, INFO_TYPE_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_HELP_TYPE.get()); |
| | | this.subCommand.addArgument(this.typeArgument); |
| | | |
| | | this.inheritedModeArgument = new BooleanArgument(OPTION_DSCFG_LONG_INHERITED, OPTION_DSCFG_SHORT_INHERITED, |
| | | OPTION_DSCFG_LONG_INHERITED, INFO_DSCFG_DESCRIPTION_HELP_INHERITED.get()); |
| | | subCommand.addArgument(inheritedModeArgument); |
| | | |
| | | // Register common arguments. |
| | | registerPropertyNameArgument(this.subCommand); |
| | | |
| | | this.categoryMap = new TreeMap<String, Map<String, AbstractManagedObjectDefinition<?, ?>>>(); |
| | | this.tagMap = new HashMap<Tag, Map<String, AbstractManagedObjectDefinition<?, ?>>>(); |
| | | |
| | | setCommandBuilderUseful(false); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** |
| | | * Registers a managed object definition with this help properties sub-command. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | */ |
| | | public void registerManagedObjectDefinition(AbstractManagedObjectDefinition<?, ?> d) { |
| | | // Determine the definition's base name. |
| | | AbstractManagedObjectDefinition<?, ?> parent = d; |
| | | while (!parent.getParent().isTop()) { |
| | | parent = parent.getParent(); |
| | | } |
| | | |
| | | String baseName = parent.getName(); |
| | | String typeName = null; |
| | | if (parent == d) { |
| | | // This was a top-level definition. |
| | | typeName = DSConfig.GENERIC_TYPE; |
| | | } else { |
| | | // For the type name we shorten it, if possible, by stripping |
| | | // off the trailing part of the name which matches the |
| | | // base-type. |
| | | String suffix = "-" + baseName; |
| | | typeName = d.getName(); |
| | | if (typeName.endsWith(suffix)) { |
| | | typeName = typeName.substring(0, typeName.length() - suffix.length()); |
| | | } |
| | | } |
| | | |
| | | // Get the sub-type mapping, creating it if necessary. |
| | | Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap.get(baseName); |
| | | if (subTypes == null) { |
| | | subTypes = new TreeMap<String, AbstractManagedObjectDefinition<?, ?>>(); |
| | | categoryMap.put(baseName, subTypes); |
| | | } |
| | | |
| | | subTypes.put(typeName, d); |
| | | |
| | | // Get the tag mapping, creating it if necessary. |
| | | for (Tag tag : d.getAllTags()) { |
| | | subTypes = tagMap.get(tag); |
| | | if (subTypes == null) { |
| | | subTypes = new TreeMap<String, AbstractManagedObjectDefinition<?, ?>>(); |
| | | tagMap.put(tag, subTypes); |
| | | } |
| | | subTypes.put(typeName, d); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | |
| | | String categoryName = categoryArgument.getValue(); |
| | | String typeName = typeArgument.getValue(); |
| | | Tag tag = null; |
| | | Set<String> propertyNames = getPropertyNames(); |
| | | |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | |
| | | // Update the command builder. |
| | | updateCommandBuilderWithSubCommand(); |
| | | |
| | | List<AbstractManagedObjectDefinition<?, ?>> dlist = new LinkedList<AbstractManagedObjectDefinition<?, ?>>(); |
| | | AbstractManagedObjectDefinition<?, ?> tmp = null; |
| | | |
| | | if (categoryName != null) { |
| | | // User requested a category of components. |
| | | Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap.get(categoryName); |
| | | |
| | | if (subTypes == null) { |
| | | // Try a tag-base look-up. |
| | | try { |
| | | tag = Tag.valueOf(categoryName); |
| | | } catch (IllegalArgumentException e) { |
| | | throw ArgumentExceptionFactory.unknownCategory(categoryName); |
| | | } |
| | | |
| | | subTypes = tagMap.get(tag); |
| | | if (subTypes == null) { |
| | | throw ArgumentExceptionFactory.unknownCategory(categoryName); |
| | | } |
| | | } else { |
| | | // Cache the generic definition for improved errors later on. |
| | | tmp = subTypes.get(DSConfig.GENERIC_TYPE); |
| | | } |
| | | |
| | | if (typeName != null) { |
| | | AbstractManagedObjectDefinition<?, ?> d = subTypes.get(typeName); |
| | | if (d == null) { |
| | | throw ArgumentExceptionFactory.unknownTypeForCategory(typeName, categoryName); |
| | | } |
| | | dlist.add(d); |
| | | |
| | | // Cache the generic definition for improved errors later on. |
| | | tmp = d; |
| | | } else { |
| | | dlist.addAll(subTypes.values()); |
| | | } |
| | | } else if (typeName != null) { |
| | | // User requested just the sub-type which could appear in |
| | | // multiple categories. |
| | | boolean isFound = false; |
| | | |
| | | for (Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes : categoryMap.values()) { |
| | | AbstractManagedObjectDefinition<?, ?> d = subTypes.get(typeName); |
| | | if (d != null) { |
| | | dlist.add(d); |
| | | isFound = true; |
| | | } |
| | | } |
| | | |
| | | if (!isFound) { |
| | | throw ArgumentExceptionFactory.unknownTypeForCategory(typeName, categoryName); |
| | | } |
| | | } else { |
| | | // User did not specify a category nor a sub-type. |
| | | for (Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes : categoryMap.values()) { |
| | | dlist.addAll(subTypes.values()); |
| | | } |
| | | } |
| | | |
| | | // Validate property names. |
| | | if (dlist.size() == 1) { |
| | | // Cache the generic definition for improved errors later on. |
| | | tmp = dlist.get(0); |
| | | } |
| | | |
| | | for (String propertyName : propertyNames) { |
| | | boolean isFound = false; |
| | | |
| | | for (AbstractManagedObjectDefinition<?, ?> d : dlist) { |
| | | try { |
| | | d.getPropertyDefinition(propertyName); |
| | | isFound = true; |
| | | } catch (IllegalArgumentException e) { |
| | | // Ignore for now. |
| | | } |
| | | } |
| | | |
| | | if (!isFound) { |
| | | if (tmp != null) { |
| | | throw ArgumentExceptionFactory.unknownProperty(tmp, propertyName); |
| | | } else { |
| | | throw ArgumentExceptionFactory.unknownProperty(propertyName); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Output everything to the output stream. |
| | | if (!app.isVerbose()) { |
| | | displayNonVerbose(app, categoryName, typeName, tag, propertyNames); |
| | | } else { |
| | | displayVerbose(app, categoryName, typeName, tag, propertyNames); |
| | | } |
| | | |
| | | return MenuResult.success(0); |
| | | } |
| | | |
| | | /** Output property summary table. */ |
| | | private void displayNonVerbose(ConsoleApplication app, String categoryName, String typeName, Tag tag, |
| | | Set<String> propertyNames) { |
| | | if (!app.isScriptFriendly()) { |
| | | displayPropertyOptionKey(app); |
| | | app.println(); |
| | | app.println(); |
| | | } |
| | | |
| | | // Headings. |
| | | TableBuilder builder = new TableBuilder(); |
| | | |
| | | builder.appendHeading(INFO_DSCFG_HEADING_COMPONENT_NAME.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_COMPONENT_TYPE.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_OPTIONS.get()); |
| | | builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get()); |
| | | |
| | | // Sort keys. |
| | | builder.addSortKey(0); |
| | | builder.addSortKey(1); |
| | | builder.addSortKey(2); |
| | | |
| | | // Generate the table content. |
| | | for (String category : categoryMap.keySet()) { |
| | | // Skip if this is the wrong category. |
| | | if (categoryName != null && !categoryName.equals(category)) { |
| | | continue; |
| | | } |
| | | |
| | | // Process the sub-types. |
| | | Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap.get(category); |
| | | for (String type : subTypes.keySet()) { |
| | | // Skip if this is the wrong sub-type. |
| | | if (typeName != null && !typeName.equals(type)) { |
| | | continue; |
| | | } |
| | | |
| | | // Display help for each property. |
| | | AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type); |
| | | |
| | | // Skip hidden types. |
| | | if (mod.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | // Skip advanced types if required. |
| | | if (!app.isAdvancedMode() && mod.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | // Skip if this does not have the required tag. |
| | | if (tag != null && !mod.hasTag(tag)) { |
| | | continue; |
| | | } |
| | | |
| | | Set<PropertyDefinition<?>> pds = new TreeSet<PropertyDefinition<?>>(); |
| | | if (inheritedModeArgument.isPresent()) { |
| | | pds.addAll(mod.getAllPropertyDefinitions()); |
| | | } else { |
| | | pds.addAll(mod.getPropertyDefinitions()); |
| | | |
| | | // The list will still contain overridden properties. |
| | | if (mod.getParent() != null) { |
| | | pds.removeAll(mod.getParent().getAllPropertyDefinitions()); |
| | | } |
| | | } |
| | | |
| | | for (PropertyDefinition<?> pd : pds) { |
| | | if (pd.hasOption(PropertyOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!propertyNames.isEmpty() && !propertyNames.contains(pd.getName())) { |
| | | continue; |
| | | } |
| | | |
| | | // Display the property. |
| | | builder.startRow(); |
| | | |
| | | // Display the component category. |
| | | builder.appendCell(category); |
| | | |
| | | // Display the component type. |
| | | builder.appendCell(type); |
| | | |
| | | // Display the property name. |
| | | builder.appendCell(pd.getName()); |
| | | |
| | | // Display the options. |
| | | builder.appendCell(getPropertyOptionSummary(pd)); |
| | | |
| | | // Display the syntax. |
| | | PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(false); |
| | | builder.appendCell(v.getUsage(pd)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | TablePrinter printer; |
| | | if (app.isScriptFriendly()) { |
| | | printer = createScriptFriendlyTablePrinter(app.getOutputStream()); |
| | | } else { |
| | | printer = new TextTablePrinter(app.getOutputStream()); |
| | | } |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** Display detailed help on managed objects and their properties. */ |
| | | private void displayVerbose(ConsoleApplication app, String categoryName, String typeName, Tag tag, |
| | | Set<String> propertyNames) { |
| | | // Construct line used to separate consecutive sections. |
| | | LocalizableMessageBuilder mb; |
| | | |
| | | mb = new LocalizableMessageBuilder(); |
| | | for (int i = 0; i < MAX_LINE_WIDTH; i++) { |
| | | mb.append('='); |
| | | } |
| | | LocalizableMessage c1 = mb.toMessage(); |
| | | |
| | | mb = new LocalizableMessageBuilder(); |
| | | for (int i = 0; i < MAX_LINE_WIDTH; i++) { |
| | | mb.append('-'); |
| | | } |
| | | LocalizableMessage c2 = mb.toMessage(); |
| | | |
| | | // Display help for each managed object. |
| | | boolean isFirstManagedObject = true; |
| | | for (String category : categoryMap.keySet()) { |
| | | // Skip if this is the wrong category. |
| | | if (categoryName != null && !categoryName.equals(category)) { |
| | | continue; |
| | | } |
| | | |
| | | // Process the sub-types. |
| | | Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap.get(category); |
| | | for (String type : subTypes.keySet()) { |
| | | // Skip if this is the wrong sub-type. |
| | | if (typeName != null && !typeName.equals(type)) { |
| | | continue; |
| | | } |
| | | |
| | | // Display help for each property. |
| | | AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type); |
| | | |
| | | // Skip hidden types. |
| | | if (mod.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | // Skip advanced types if required. |
| | | if (!app.isAdvancedMode() && mod.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | // Skip if this does not have the required tag. |
| | | if (tag != null && !mod.hasTag(tag)) { |
| | | continue; |
| | | } |
| | | |
| | | Set<PropertyDefinition<?>> pds = new TreeSet<PropertyDefinition<?>>(); |
| | | if (inheritedModeArgument.isPresent()) { |
| | | pds.addAll(mod.getAllPropertyDefinitions()); |
| | | } else { |
| | | pds.addAll(mod.getPropertyDefinitions()); |
| | | |
| | | // The list will still contain overridden properties. |
| | | if (mod.getParent() != null) { |
| | | pds.removeAll(mod.getParent().getAllPropertyDefinitions()); |
| | | } |
| | | } |
| | | |
| | | boolean isFirstProperty = true; |
| | | for (PropertyDefinition<?> pd : pds) { |
| | | if (pd.hasOption(PropertyOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!propertyNames.isEmpty() && !propertyNames.contains(pd.getName())) { |
| | | continue; |
| | | } |
| | | |
| | | if (isFirstProperty) { |
| | | // User has requested properties relating to this managed |
| | | // object definition, so display the summary of the |
| | | // managed |
| | | // object. |
| | | if (!isFirstManagedObject) { |
| | | app.println(); |
| | | app.println(c1); |
| | | app.println(); |
| | | } else { |
| | | isFirstManagedObject = false; |
| | | } |
| | | |
| | | // Display the title. |
| | | app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(mod.getUserFriendlyName())); |
| | | |
| | | app.println(); |
| | | app.println(mod.getSynopsis()); |
| | | if (mod.getDescription() != null) { |
| | | app.println(); |
| | | app.println(mod.getDescription()); |
| | | } |
| | | } |
| | | |
| | | app.println(); |
| | | app.println(c2); |
| | | app.println(); |
| | | |
| | | displayVerboseSingleProperty(app, mod, pd.getName()); |
| | | isFirstProperty = false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | |
| | | import javax.net.ssl.SSLException; |
| | | |
| | | import org.forgerock.opendj.config.LDAPProfile; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.config.client.ldap.LDAPManagementContext; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.CommandBuilder; |
| | | import com.forgerock.opendj.cli.ConnectionFactoryProvider; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.ReturnCode; |
| | | |
| | | /** |
| | | * An LDAP management context factory for the DSConfig tool. |
| | | */ |
| | | public final class LDAPManagementContextFactory |
| | | { |
| | | |
| | | /** The management context. */ |
| | | private ManagementContext context; |
| | | |
| | | /** The connection parameters command builder. */ |
| | | private CommandBuilder contextCommandBuilder; |
| | | |
| | | /** The connection factory provider. */ |
| | | private final ConnectionFactoryProvider provider; |
| | | |
| | | /** The connection factory. */ |
| | | private final ConnectionFactory factory; |
| | | |
| | | /** |
| | | * Creates a new LDAP management context factory based on an authenticated |
| | | * connection factory. |
| | | * |
| | | * @param cfp |
| | | * The connection factory provider which should be used in this |
| | | * context. |
| | | * @throws ArgumentException |
| | | * If an exception occurs when creating the authenticated connection |
| | | * factory linked to this context. |
| | | */ |
| | | public LDAPManagementContextFactory(ConnectionFactoryProvider cfp) throws ArgumentException { |
| | | this.provider = cfp; |
| | | factory = cfp.getAuthenticatedConnectionFactory(); |
| | | } |
| | | |
| | | /** |
| | | * Closes this management context. |
| | | */ |
| | | public void close() |
| | | { |
| | | closeSilently(context); |
| | | } |
| | | |
| | | /** |
| | | * Returns the command builder that provides the equivalent arguments in |
| | | * interactive mode to get the management context. |
| | | * |
| | | * @return the command builder that provides the equivalent arguments in |
| | | * interactive mode to get the management context. |
| | | */ |
| | | public CommandBuilder getContextCommandBuilder() |
| | | { |
| | | return contextCommandBuilder; |
| | | } |
| | | |
| | | /** |
| | | * Gets the management context which sub-commands should use in |
| | | * order to manage the directory server. |
| | | * |
| | | * @param app |
| | | * The console application instance. |
| | | * @return Returns the management context which sub-commands should |
| | | * use in order to manage the directory server. |
| | | * @throws ArgumentException |
| | | * If a management context related argument could not be |
| | | * parsed successfully. |
| | | * @throws ClientException |
| | | * If the management context could not be created. |
| | | */ |
| | | public ManagementContext getManagementContext(ConsoleApplication app) |
| | | throws ArgumentException, ClientException |
| | | { |
| | | // Lazily create the LDAP management context. |
| | | if (context == null) |
| | | { |
| | | Connection connection; |
| | | final String hostName = provider.getHostname(); |
| | | final int port = provider.getPort(); |
| | | try |
| | | { |
| | | connection = factory.getConnection(); |
| | | BuildVersion.checkVersionMismatch(connection); |
| | | } |
| | | catch (ErrorResultException e) |
| | | { |
| | | if (e.getCause() instanceof SSLException) |
| | | { |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | ERR_FAILED_TO_CONNECT_NOT_TRUSTED.get(hostName, String |
| | | .valueOf(port))); |
| | | } |
| | | else |
| | | { |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName, String |
| | | .valueOf(port))); |
| | | } |
| | | } |
| | | catch (ConfigException e) |
| | | { |
| | | throw new ClientException(ReturnCode.ERROR_USER_DATA,e.getMessageObject()); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName, port)); |
| | | } |
| | | finally |
| | | { |
| | | closeSilently(factory); |
| | | } |
| | | |
| | | context = |
| | | LDAPManagementContext.newManagementContext(connection, LDAPProfile |
| | | .getInstance()); |
| | | } |
| | | return context; |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2012-2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.LIST_TABLE_SEPARATOR; |
| | | |
| | | import java.io.PrintStream; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.SortedMap; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeMap; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectOption; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | 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.TableBuilder; |
| | | import com.forgerock.opendj.cli.TablePrinter; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to list existing managed objects. |
| | | * <p> |
| | | * This sub-command implements the various list-xxx sub-commands. |
| | | */ |
| | | final class ListSubCommandHandler extends SubCommandHandler { |
| | | |
| | | /** |
| | | * Creates a new list-xxx sub-command for an instantiable relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The instantiable relation. |
| | | * @return Returns the new list-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static ListSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | InstantiableRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new ListSubCommandHandler(parser, p, r, r.getPluralName(), r.getUserFriendlyPluralName()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new list-xxx sub-command for a set relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The set relation. |
| | | * @return Returns the new list-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static ListSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | SetRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new ListSubCommandHandler(parser, p, r, r.getPluralName(), r.getUserFriendlyPluralName()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new list-xxx sub-command for an optional relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param p |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The optional relation. |
| | | * @return Returns the new list-xxx sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static ListSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | OptionalRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new ListSubCommandHandler(parser, p, r, r.getName(), r.getUserFriendlyName()); |
| | | } |
| | | |
| | | /** The sub-commands naming arguments. */ |
| | | private final List<StringArgument> namingArgs; |
| | | |
| | | /** The path of the parent managed object. */ |
| | | private final ManagedObjectPath<?, ?> path; |
| | | |
| | | /** The relation which should be listed. */ |
| | | private final RelationDefinition<?, ?> relation; |
| | | |
| | | /** The sub-command associated with this handler. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** Private constructor. */ |
| | | private ListSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, |
| | | RelationDefinition<?, ?> r, String rname, LocalizableMessage rufn) throws ArgumentException { |
| | | path = p; |
| | | relation = r; |
| | | |
| | | // Create the sub-command. |
| | | subCommand = new SubCommand(parser, "list-" + rname, false, 0, 0, null, |
| | | INFO_DSCFG_DESCRIPTION_SUBCMD_LIST.get(rufn)); |
| | | |
| | | // Create the naming arguments. |
| | | namingArgs = createNamingArgs(subCommand, path, false); |
| | | |
| | | // Register arguments. |
| | | registerPropertyNameArgument(subCommand); |
| | | registerUnitSizeArgument(subCommand); |
| | | registerUnitTimeArgument(subCommand); |
| | | |
| | | // Register the tags associated with the child managed objects. |
| | | addTags(relation.getChildDefinition().getAllTags()); |
| | | } |
| | | |
| | | /** |
| | | * Gets the relation definition associated with the type of component that this sub-command handles. |
| | | * |
| | | * @return Returns the relation definition associated with the type of component that this sub-command handles. |
| | | */ |
| | | public RelationDefinition<?, ?> getRelationDefinition() { |
| | | return relation; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | // Get the property names. |
| | | Set<String> propertyNames = getPropertyNames(); |
| | | |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | |
| | | // Update the command builder. |
| | | updateCommandBuilderWithSubCommand(); |
| | | |
| | | if (propertyNames.isEmpty()) { |
| | | // Use a default set of properties. |
| | | propertyNames = CLIProfile.getInstance().getDefaultListPropertyNames(relation); |
| | | } |
| | | |
| | | PropertyValuePrinter valuePrinter = new PropertyValuePrinter(getSizeUnit(), getTimeUnit(), |
| | | app.isScriptFriendly()); |
| | | |
| | | // Get the naming argument values. |
| | | List<String> names = getNamingArgValues(app, namingArgs); |
| | | |
| | | LocalizableMessage ufn; |
| | | if (relation instanceof InstantiableRelationDefinition) { |
| | | InstantiableRelationDefinition<?, ?> irelation = (InstantiableRelationDefinition<?, ?>) relation; |
| | | ufn = irelation.getUserFriendlyPluralName(); |
| | | } else if (relation instanceof SetRelationDefinition) { |
| | | SetRelationDefinition<?, ?> srelation = (SetRelationDefinition<?, ?>) relation; |
| | | ufn = srelation.getUserFriendlyPluralName(); |
| | | } else { |
| | | ufn = relation.getUserFriendlyName(); |
| | | } |
| | | |
| | | // List the children. |
| | | ManagementContext context = factory.getManagementContext(app); |
| | | MenuResult<ManagedObject<?>> result; |
| | | try { |
| | | result = getManagedObject(app, context, path, names); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | ufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | ufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | ufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(ufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | |
| | | ManagedObject<?> parent = result.getValue(); |
| | | SortedMap<String, ManagedObject<?>> children = new TreeMap<String, ManagedObject<?>>(); |
| | | if (relation instanceof InstantiableRelationDefinition) { |
| | | InstantiableRelationDefinition<?, ?> irelation = (InstantiableRelationDefinition<?, ?>) relation; |
| | | try { |
| | | for (String s : parent.listChildren(irelation)) { |
| | | try { |
| | | children.put(s, parent.getChild(irelation, s)); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // Ignore - as it's been removed since we did the list. |
| | | } |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | // FIXME: just output this as a warnings (incl. the name) but |
| | | // continue. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // FIXME: just output this as a warnings (incl. the name) but |
| | | // continue. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } |
| | | } else if (relation instanceof SetRelationDefinition) { |
| | | SetRelationDefinition<?, ?> srelation = (SetRelationDefinition<?, ?>) relation; |
| | | try { |
| | | for (String s : parent.listChildren(srelation)) { |
| | | try { |
| | | children.put(s, parent.getChild(srelation, s)); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // Ignore - as it's been removed since we did the list. |
| | | } |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | // FIXME: just output this as a warnings (incl. the name) but |
| | | // continue. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // FIXME: just output this as a warnings (incl. the name) but |
| | | // continue. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } |
| | | } else if (relation instanceof OptionalRelationDefinition) { |
| | | OptionalRelationDefinition<?, ?> orelation = (OptionalRelationDefinition<?, ?>) relation; |
| | | try { |
| | | if (parent.hasChild(orelation)) { |
| | | ManagedObject<?> child = parent.getChild(orelation); |
| | | children.put(child.getManagedObjectDefinition().getName(), child); |
| | | } else { |
| | | // Indicate that the managed object does not exist. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(ufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_LIST_MONFE.get(ufn); |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } |
| | | |
| | | // Output the results. |
| | | if (app.isScriptFriendly()) { |
| | | // Output just the names of the children. |
| | | for (String name : children.keySet()) { |
| | | // Skip advanced and hidden components in non-advanced mode. |
| | | if (!app.isAdvancedMode()) { |
| | | ManagedObject<?> child = children.get(name); |
| | | ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition(); |
| | | |
| | | if (d.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (d.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | app.println(LocalizableMessage.raw(name)); |
| | | } |
| | | } else { |
| | | // Create a table of their properties containing the name, type (if |
| | | // appropriate), and requested properties. |
| | | SortedMap<String, ?> subTypes = getSubTypes(relation.getChildDefinition()); |
| | | boolean includeTypesColumn = subTypes.size() != 1 || !subTypes.containsKey(DSConfig.GENERIC_TYPE); |
| | | |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.appendHeading(relation.getUserFriendlyName()); |
| | | if (includeTypesColumn) { |
| | | builder.appendHeading(INFO_DSCFG_HEADING_COMPONENT_TYPE.get()); |
| | | } |
| | | for (String propertyName : propertyNames) { |
| | | builder.appendHeading(LocalizableMessage.raw(propertyName)); |
| | | } |
| | | builder.addSortKey(0); |
| | | |
| | | String baseType = relation.getChildDefinition().getName(); |
| | | String typeSuffix = "-" + baseType; |
| | | for (String name : children.keySet()) { |
| | | ManagedObject<?> child = children.get(name); |
| | | ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition(); |
| | | |
| | | // Skip advanced and hidden components in non-advanced mode. |
| | | if (!app.isAdvancedMode()) { |
| | | if (d.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (d.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | // First output the name. |
| | | builder.startRow(); |
| | | if (relation instanceof SetRelationDefinition) { |
| | | builder.appendCell(d.getUserFriendlyName()); |
| | | } else { |
| | | builder.appendCell(name); |
| | | } |
| | | |
| | | if (includeTypesColumn) { |
| | | // Output the managed object type in the form used in |
| | | // create-xxx commands. |
| | | String childType = d.getName(); |
| | | boolean isCustom = CLIProfile.getInstance().isForCustomization(d); |
| | | if (baseType.equals(childType)) { |
| | | if (isCustom) { |
| | | builder.appendCell(DSConfig.CUSTOM_TYPE); |
| | | } else { |
| | | builder.appendCell(DSConfig.GENERIC_TYPE); |
| | | } |
| | | } else if (childType.endsWith(typeSuffix)) { |
| | | String ctname = childType.substring(0, childType.length() - typeSuffix.length()); |
| | | if (isCustom) { |
| | | ctname = String.format("%s-%s", DSConfig.CUSTOM_TYPE, ctname); |
| | | } |
| | | builder.appendCell(ctname); |
| | | } else { |
| | | builder.appendCell(childType); |
| | | } |
| | | } |
| | | |
| | | // Now any requested properties. |
| | | for (String propertyName : propertyNames) { |
| | | try { |
| | | PropertyDefinition<?> pd = d.getPropertyDefinition(propertyName); |
| | | displayProperty(app, builder, child, pd, valuePrinter); |
| | | } catch (IllegalArgumentException e) { |
| | | // Assume this child managed object does not support this |
| | | // property. |
| | | if (app.isScriptFriendly()) { |
| | | builder.appendCell(); |
| | | } else { |
| | | builder.appendCell("-"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | PrintStream out = app.getOutputStream(); |
| | | if (app.isScriptFriendly()) { |
| | | TablePrinter printer = createScriptFriendlyTablePrinter(out); |
| | | builder.print(printer); |
| | | } else { |
| | | if (app.isInteractive()) { |
| | | // Make interactive mode prettier. |
| | | app.println(); |
| | | app.println(); |
| | | } |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | printer.setColumnSeparator(LIST_TABLE_SEPARATOR); |
| | | builder.print(printer); |
| | | } |
| | | } |
| | | |
| | | return MenuResult.success(0); |
| | | } |
| | | |
| | | /** Display the set of values associated with a property. */ |
| | | private <T> void displayProperty(ConsoleApplication app, TableBuilder builder, ManagedObject<?> mo, |
| | | PropertyDefinition<T> pd, PropertyValuePrinter valuePrinter) { |
| | | SortedSet<T> values = mo.getPropertyValues(pd); |
| | | if (values.isEmpty()) { |
| | | if (app.isScriptFriendly()) { |
| | | builder.appendCell(); |
| | | } else { |
| | | builder.appendCell("-"); |
| | | } |
| | | } else { |
| | | StringBuilder sb = new StringBuilder(); |
| | | boolean isFirst = true; |
| | | for (T value : values) { |
| | | if (!isFirst) { |
| | | sb.append(", "); |
| | | } |
| | | sb.append(valuePrinter.print(pd, value)); |
| | | isFirst = false; |
| | | } |
| | | |
| | | builder.appendCell(sb.toString()); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | |
| | | /** |
| | | * This class is a data structure that can be used as an interface between PropertyValueEditor and the different |
| | | * handlers that call it. Since PropertyValueEditor is not aware of the different options used by the handlers it cannot |
| | | * directly construct a CommandBuilder, but PropertyValueEditor knows about the different changes (set, reset, delete) |
| | | * that can be performed against the ManagedObject and these changes can be used to generate a CommandBuilder. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | */ |
| | | final class PropertyEditorModification<T> { |
| | | /** |
| | | * The enumeration that describes the different types of modifications that we can have. |
| | | */ |
| | | enum Type { |
| | | /** The user chose to set values. */ |
| | | SET, |
| | | /** The user chose to reset values. */ |
| | | RESET, |
| | | /** The user chose to add values. */ |
| | | ADD, |
| | | /** The user chose to delete values. */ |
| | | REMOVE |
| | | }; |
| | | |
| | | private PropertyDefinition<T> propertyDefinition; |
| | | private Type type; |
| | | private SortedSet<T> values; |
| | | private SortedSet<T> originalValues; |
| | | |
| | | /** |
| | | * The private constructor of the PropertyEditorModification. |
| | | * |
| | | * @param propertyDefinition |
| | | * the property definition associated with the modification. |
| | | * @param type |
| | | * the type of the modification. |
| | | * @param values |
| | | * the values associated with the modifications. |
| | | * @param originalValues |
| | | * the original values of the property we are modifying. |
| | | */ |
| | | private PropertyEditorModification(PropertyDefinition<T> propertyDefinition, Type type, SortedSet<T> values, |
| | | SortedSet<T> originalValues) { |
| | | this.propertyDefinition = propertyDefinition; |
| | | this.type = type; |
| | | this.values = new TreeSet<T>(values); |
| | | this.originalValues = new TreeSet<T>(originalValues); |
| | | } |
| | | |
| | | /** |
| | | * Creates a reset modification. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property. |
| | | * @param propertyDefinition |
| | | * the property that is modified. |
| | | * @param originalValues |
| | | * the original values of the property. |
| | | * @return a reset modification for a given property. |
| | | */ |
| | | static <T> PropertyEditorModification<T> createResetModification(PropertyDefinition<T> propertyDefinition, |
| | | SortedSet<T> originalValues) { |
| | | return new PropertyEditorModification<T>(propertyDefinition, Type.RESET, new TreeSet<T>(propertyDefinition), |
| | | originalValues); |
| | | } |
| | | |
| | | /** |
| | | * Creates an add modification. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property. |
| | | * @param propertyDefinition |
| | | * the property that is modified. |
| | | * @param addedValues |
| | | * the values that are added in this modification. |
| | | * @param originalValues |
| | | * the original values of the property. |
| | | * @return a reset modification for a given property. |
| | | */ |
| | | static <T> PropertyEditorModification<T> createAddModification(PropertyDefinition<T> propertyDefinition, |
| | | SortedSet<T> addedValues, SortedSet<T> originalValues) { |
| | | return new PropertyEditorModification<T>(propertyDefinition, Type.ADD, addedValues, originalValues); |
| | | } |
| | | |
| | | /** |
| | | * Creates a set modification. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property. |
| | | * @param propertyDefinition |
| | | * the property that is modified. |
| | | * @param newValues |
| | | * the new values for the property. |
| | | * @param originalValues |
| | | * the original values of the property. |
| | | * @return a reset modification for a given property. |
| | | */ |
| | | static <T> PropertyEditorModification<T> createSetModification(PropertyDefinition<T> propertyDefinition, |
| | | SortedSet<T> newValues, SortedSet<T> originalValues) { |
| | | return new PropertyEditorModification<T>(propertyDefinition, Type.SET, newValues, originalValues); |
| | | } |
| | | |
| | | /** |
| | | * Creates a remove modification. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property. |
| | | * @param propertyDefinition |
| | | * the property that is modified. |
| | | * @param removedValues |
| | | * the values that are removed in this modification. |
| | | * @param originalValues |
| | | * the original values of the property. |
| | | * @return a reset modification for a given property. |
| | | */ |
| | | static <T> PropertyEditorModification<T> createRemoveModification(PropertyDefinition<T> propertyDefinition, |
| | | SortedSet<T> removedValues, SortedSet<T> originalValues) { |
| | | return new PropertyEditorModification<T>(propertyDefinition, Type.REMOVE, removedValues, originalValues); |
| | | } |
| | | |
| | | /** |
| | | * Retuns the property definition associated with this modification. |
| | | * |
| | | * @return the property definition associated with this modification. |
| | | */ |
| | | PropertyDefinition<T> getPropertyDefinition() { |
| | | return propertyDefinition; |
| | | } |
| | | |
| | | /** |
| | | * Returns the type of the modification. |
| | | * |
| | | * @return the type of the modification. |
| | | */ |
| | | Type getType() { |
| | | return type; |
| | | } |
| | | |
| | | /** |
| | | * Returns the specific values associated with the modification. |
| | | * |
| | | * @return the specific values associated with the modification. |
| | | */ |
| | | SortedSet<T> getModificationValues() { |
| | | return values; |
| | | } |
| | | |
| | | /** |
| | | * Returns the original values associated with the property. |
| | | * |
| | | * @return the original values associated with the property. |
| | | */ |
| | | SortedSet<T> getOriginalValues() { |
| | | return originalValues; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String toString() { |
| | | return "Property name: " + getPropertyDefinition() + "\nMod type: " + getType() + "\nMod values: " |
| | | + getModificationValues() + "\nOriginal values: " + getOriginalValues(); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.EnumSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.AggregationPropertyDefinition; |
| | | import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.BooleanPropertyDefinition; |
| | | import org.forgerock.opendj.config.Configuration; |
| | | import org.forgerock.opendj.config.ConfigurationClient; |
| | | import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; |
| | | import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.EnumPropertyDefinition; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; |
| | | import org.forgerock.opendj.config.PropertyDefinitionVisitor; |
| | | import org.forgerock.opendj.config.PropertyException; |
| | | import org.forgerock.opendj.config.PropertyOption; |
| | | import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | import org.forgerock.util.Reject; |
| | | |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.HelpCallback; |
| | | 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.TableBuilder; |
| | | import com.forgerock.opendj.cli.TextTablePrinter; |
| | | |
| | | /** |
| | | * Common methods used for interactively editing properties. |
| | | */ |
| | | final class PropertyValueEditor { |
| | | |
| | | /** |
| | | * A menu call-back which can be used to dynamically create new components when configuring aggregation based |
| | | * properties. |
| | | */ |
| | | private final class CreateComponentCallback<C extends ConfigurationClient, S extends Configuration> implements |
| | | MenuCallback<String> { |
| | | |
| | | /** The aggregation property definition. */ |
| | | private final AggregationPropertyDefinition<C, S> pd; |
| | | |
| | | /** |
| | | * Creates a new component create call-back for the provided aggregation property definition. |
| | | */ |
| | | private CreateComponentCallback(AggregationPropertyDefinition<C, S> pd) { |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<String> invoke(ConsoleApplication app) throws ClientException { |
| | | try { |
| | | // First get the parent managed object. |
| | | InstantiableRelationDefinition<?, ?> rd = pd.getRelationDefinition(); |
| | | ManagedObjectPath<?, ?> path = pd.getParentPath(); |
| | | LocalizableMessage ufn = rd.getUserFriendlyName(); |
| | | |
| | | ManagedObject<?> parent; |
| | | try { |
| | | parent = context.getManagedObject(path); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(pufn, pufn, pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(pufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(pufn); |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(msg); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | } |
| | | |
| | | // Now let the user create the child component. |
| | | app.println(); |
| | | app.println(); |
| | | return CreateSubCommandHandler.createManagedObject(app, context, parent, rd); |
| | | } catch (ClientException e) { |
| | | // FIXME: should really do something better with the exception |
| | | // handling here. For example, if a authz or communications |
| | | // exception occurs then the application should exit. |
| | | app.println(); |
| | | app.println(e.getMessageObject()); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A help call-back which displays a description and summary of a component and its properties. |
| | | */ |
| | | private static final class ComponentHelpCallback implements HelpCallback { |
| | | |
| | | /** The managed object being edited. */ |
| | | private final ManagedObject<?> mo; |
| | | |
| | | /** The properties that can be edited. */ |
| | | private final Collection<PropertyDefinition<?>> properties; |
| | | |
| | | /** Creates a new component helper for the specified property. */ |
| | | private ComponentHelpCallback(ManagedObject<?> mo, Collection<PropertyDefinition<?>> c) { |
| | | this.mo = mo; |
| | | this.properties = c; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void display(ConsoleApplication app) { |
| | | app.println(); |
| | | HelpSubCommandHandler.displaySingleComponent(app, mo, properties); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A simple interface for querying and retrieving common default behavior properties. |
| | | */ |
| | | private static final class DefaultBehaviorQuery<T> { |
| | | |
| | | /** |
| | | * The type of default behavior. |
| | | */ |
| | | private enum Type { |
| | | /** |
| | | * Alias default behavior. |
| | | */ |
| | | ALIAS, |
| | | |
| | | /** |
| | | * Defined default behavior. |
| | | */ |
| | | DEFINED, |
| | | |
| | | /** |
| | | * Inherited default behavior. |
| | | */ |
| | | INHERITED, |
| | | |
| | | /** |
| | | * Undefined default behavior. |
| | | */ |
| | | UNDEFINED; |
| | | } |
| | | |
| | | /** |
| | | * Create a new default behavior query object based on the provied property definition. |
| | | * |
| | | * @param <T> |
| | | * The type of property definition. |
| | | * @param pd |
| | | * The property definition. |
| | | * @return The default behavior query object. |
| | | */ |
| | | public static <T> DefaultBehaviorQuery<T> query(PropertyDefinition<T> pd) { |
| | | DefaultBehaviorProviderVisitor<T, DefaultBehaviorQuery<T>, PropertyDefinition<T>> visitor |
| | | = new DefaultBehaviorProviderVisitor<T, DefaultBehaviorQuery<T>, PropertyDefinition<T>>() { |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DefaultBehaviorQuery<T> visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | AbstractManagedObjectDefinition<?, ?> mod = d.getManagedObjectDefinition(); |
| | | String propertyName = d.getPropertyName(); |
| | | PropertyDefinition<?> pd2 = mod.getPropertyDefinition(propertyName); |
| | | |
| | | DefaultBehaviorQuery<?> query = query(pd2); |
| | | return new DefaultBehaviorQuery<T>(Type.INHERITED, query.getAliasDescription()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DefaultBehaviorQuery<T> visitAlias(AliasDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | return new DefaultBehaviorQuery<T>(Type.ALIAS, d.getSynopsis()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DefaultBehaviorQuery<T> visitDefined(DefinedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | return new DefaultBehaviorQuery<T>(Type.DEFINED, null); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DefaultBehaviorQuery<T> visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | AbstractManagedObjectDefinition<?, ?> mod = d.getManagedObjectDefinition(); |
| | | String propertyName = d.getPropertyName(); |
| | | PropertyDefinition<?> pd2 = mod.getPropertyDefinition(propertyName); |
| | | |
| | | DefaultBehaviorQuery<?> query = query(pd2); |
| | | return new DefaultBehaviorQuery<T>(Type.INHERITED, query.getAliasDescription()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DefaultBehaviorQuery<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, |
| | | PropertyDefinition<T> p) { |
| | | return new DefaultBehaviorQuery<T>(Type.UNDEFINED, null); |
| | | } |
| | | }; |
| | | |
| | | return pd.getDefaultBehaviorProvider().accept(visitor, pd); |
| | | } |
| | | |
| | | /** |
| | | * The description of the behavior if it is an alias default behavior. |
| | | */ |
| | | private final LocalizableMessage aliasDescription; |
| | | |
| | | /** The type of behavior. */ |
| | | private final Type type; |
| | | |
| | | /** Private constructor. */ |
| | | private DefaultBehaviorQuery(Type type, LocalizableMessage aliasDescription) { |
| | | this.type = type; |
| | | this.aliasDescription = aliasDescription; |
| | | } |
| | | |
| | | /** |
| | | * Gets the detailed description of this default behavior if it is an alias default behavior or if it inherits |
| | | * from an alias default behavior. |
| | | * |
| | | * @return Returns the detailed description of this default behavior if it is an alias default behavior or if it |
| | | * inherits from an alias default behavior, otherwise <code>null</code>. |
| | | */ |
| | | public LocalizableMessage getAliasDescription() { |
| | | return aliasDescription; |
| | | } |
| | | |
| | | /** |
| | | * Determines whether or not the default behavior is alias. |
| | | * |
| | | * @return Returns <code>true</code> if the default behavior is alias. |
| | | */ |
| | | public boolean isAlias() { |
| | | return type == Type.ALIAS; |
| | | } |
| | | |
| | | /** |
| | | * Determines whether or not the default behavior is defined. |
| | | * |
| | | * @return Returns <code>true</code> if the default behavior is defined. |
| | | */ |
| | | public boolean isDefined() { |
| | | return type == Type.DEFINED; |
| | | } |
| | | |
| | | /** |
| | | * Determines whether or not the default behavior is inherited. |
| | | * |
| | | * @return Returns <code>true</code> if the default behavior is inherited. |
| | | */ |
| | | public boolean isInherited() { |
| | | return type == Type.INHERITED; |
| | | } |
| | | |
| | | /** |
| | | * Determines whether or not the default behavior is undefined. |
| | | * |
| | | * @return Returns <code>true</code> if the default behavior is undefined. |
| | | */ |
| | | public boolean isUndefined() { |
| | | return type == Type.UNDEFINED; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * A property definition visitor which initializes mandatory properties. |
| | | */ |
| | | private final class MandatoryPropertyInitializer extends PropertyDefinitionVisitor<MenuResult<Void>, Void> |
| | | implements MenuCallback<Void> { |
| | | |
| | | /** Any exception that was caught during processing. */ |
| | | private ClientException e = null; |
| | | |
| | | /** The managed object being edited. */ |
| | | private final ManagedObject<?> mo; |
| | | |
| | | /** The property to be edited. */ |
| | | private final PropertyDefinition<?> pd; |
| | | |
| | | /** Creates a new property editor for the specified property. */ |
| | | private MandatoryPropertyInitializer(ManagedObject<?> mo, PropertyDefinition<?> pd) { |
| | | this.mo = mo; |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Void> invoke(ConsoleApplication app) throws ClientException { |
| | | displayPropertyHeader(app, pd); |
| | | |
| | | MenuResult<Void> result = pd.accept(this, null); |
| | | |
| | | if (e != null) { |
| | | throw e; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <C extends ConfigurationClient, S extends Configuration> MenuResult<Void> visitAggregation( |
| | | AggregationPropertyDefinition<C, S> d, Void p) { |
| | | MenuBuilder<String> builder = new MenuBuilder<String>(app); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | InstantiableRelationDefinition<C, S> rd = d.getRelationDefinition(); |
| | | if (d.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_COMPONENT_MULTI.get(rd.getUserFriendlyPluralName(), |
| | | d.getName())); |
| | | builder.setAllowMultiSelect(true); |
| | | } else { |
| | | builder.setPrompt( |
| | | INFO_EDITOR_PROMPT_SELECT_COMPONENT_SINGLE.get(rd.getUserFriendlyName(), d.getName())); |
| | | } |
| | | |
| | | // Create a list of possible names. |
| | | Set<String> values = new TreeSet<String>(d); |
| | | ManagedObjectPath<?, ?> path = d.getParentPath(); |
| | | try { |
| | | values.addAll(Arrays.asList(context.listManagedObjects(path, rd))); |
| | | } catch (AuthorizationException e) { |
| | | this.e = new ClientException( |
| | | ReturnCode.CLIENT_SIDE_PARAM_ERROR, LocalizableMessage.raw(e.getMessage())); |
| | | return MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | this.e = new ClientException(ReturnCode.NO_SUCH_OBJECT, e.getMessageObject()); |
| | | return MenuResult.cancel(); |
| | | } catch (ErrorResultException e) { |
| | | this.e = new ClientException(ReturnCode.APPLICATION_ERROR, LocalizableMessage.raw(e.getMessage())); |
| | | return MenuResult.quit(); |
| | | } |
| | | |
| | | for (String value : values) { |
| | | LocalizableMessage option = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(option, MenuResult.success(value)); |
| | | } |
| | | MenuCallback<String> callback = new CreateComponentCallback<C, S>(d); |
| | | builder.addNumberedOption( |
| | | INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT.get(rd.getUserFriendlyName()), callback); |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(true); |
| | | } |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<String> menu = builder.toMenu(); |
| | | try { |
| | | app.println(); |
| | | MenuResult<String> result = menu.run(); |
| | | |
| | | if (result.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | Collection<String> newValues = result.getValues(); |
| | | SortedSet<String> oldValues = new TreeSet<String>(mo.getPropertyValues(d)); |
| | | mo.setPropertyValues(d, newValues); |
| | | isLastChoiceReset = false; |
| | | registerModification(d, new TreeSet<String>(newValues), oldValues); |
| | | return MenuResult.success(); |
| | | } |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * /** {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public MenuResult<Void> visitBoolean(BooleanPropertyDefinition d, Void p) { |
| | | MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_SINGLE.get(d.getName())); |
| | | |
| | | builder.addNumberedOption(INFO_VALUE_TRUE.get(), MenuResult.success(true)); |
| | | builder.addNumberedOption(INFO_VALUE_FALSE.get(), MenuResult.success(false)); |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(true); |
| | | } |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<Boolean> menu = builder.toMenu(); |
| | | try { |
| | | app.println(); |
| | | MenuResult<Boolean> result = menu.run(); |
| | | |
| | | if (result.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | Collection<Boolean> newValues = result.getValues(); |
| | | SortedSet<Boolean> oldValues = new TreeSet<Boolean>(mo.getPropertyValues(d)); |
| | | mo.setPropertyValues(d, newValues); |
| | | isLastChoiceReset = false; |
| | | registerModification(d, new TreeSet<Boolean>(newValues), oldValues); |
| | | return MenuResult.success(); |
| | | } |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <E extends Enum<E>> MenuResult<Void> visitEnum(EnumPropertyDefinition<E> d, Void x) { |
| | | MenuBuilder<E> builder = new MenuBuilder<E>(app); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | if (d.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_MULTI.get(d.getName())); |
| | | builder.setAllowMultiSelect(true); |
| | | } else { |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_SINGLE.get(d.getName())); |
| | | } |
| | | |
| | | Set<E> values = new TreeSet<E>(d); |
| | | values.addAll(EnumSet.allOf(d.getEnumClass())); |
| | | for (E value : values) { |
| | | LocalizableMessage option = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(option, MenuResult.success(value)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(true); |
| | | } |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<E> menu = builder.toMenu(); |
| | | try { |
| | | app.println(); |
| | | MenuResult<E> result = menu.run(); |
| | | |
| | | if (result.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | Collection<E> newValues = result.getValues(); |
| | | SortedSet<E> oldValues = new TreeSet<E>(mo.getPropertyValues(d)); |
| | | mo.setPropertyValues(d, newValues); |
| | | isLastChoiceReset = false; |
| | | registerModification(d, new TreeSet<E>(newValues), oldValues); |
| | | return MenuResult.success(); |
| | | } |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> MenuResult<Void> visitUnknown(PropertyDefinition<T> d, Void p) { |
| | | app.println(); |
| | | displayPropertySyntax(app, d); |
| | | |
| | | // Set the new property value(s). |
| | | try { |
| | | SortedSet<T> values = readPropertyValues(app, mo.getManagedObjectDefinition(), d); |
| | | SortedSet<T> oldValues = new TreeSet<T>(mo.getPropertyValues(d)); |
| | | mo.setPropertyValues(d, values); |
| | | isLastChoiceReset = false; |
| | | registerModification(d, values, oldValues); |
| | | return MenuResult.success(); |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * A menu call-back for editing a modifiable multi-valued property. |
| | | */ |
| | | private final class MultiValuedPropertyEditor extends PropertyDefinitionVisitor<MenuResult<Boolean>, Void> |
| | | implements MenuCallback<Boolean> { |
| | | |
| | | /** Any exception that was caught during processing. */ |
| | | private ClientException e = null; |
| | | |
| | | /** The managed object being edited. */ |
| | | private final ManagedObject<?> mo; |
| | | |
| | | /** The property to be edited. */ |
| | | private final PropertyDefinition<?> pd; |
| | | |
| | | /** Creates a new property editor for the specified property. */ |
| | | private MultiValuedPropertyEditor(ManagedObject<?> mo, PropertyDefinition<?> pd) { |
| | | Reject.ifFalse(pd.hasOption(PropertyOption.MULTI_VALUED)); |
| | | |
| | | this.mo = mo; |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | displayPropertyHeader(app, pd); |
| | | |
| | | MenuResult<Boolean> result = pd.accept(this, null); |
| | | if (e != null) { |
| | | throw e; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <C extends ConfigurationClient, S extends Configuration> MenuResult<Boolean> visitAggregation( |
| | | final AggregationPropertyDefinition<C, S> d, Void p) { |
| | | final SortedSet<String> defaultValues = mo.getPropertyDefaultValues(d); |
| | | final SortedSet<String> oldValues = mo.getPropertyValues(d); |
| | | final SortedSet<String> currentValues = mo.getPropertyValues(d); |
| | | final InstantiableRelationDefinition<C, S> rd = d.getRelationDefinition(); |
| | | final LocalizableMessage ufpn = rd.getUserFriendlyPluralName(); |
| | | |
| | | boolean isFirst = true; |
| | | while (true) { |
| | | if (!isFirst) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT.get(d.getName())); |
| | | } else { |
| | | isFirst = false; |
| | | } |
| | | |
| | | if (currentValues.size() > 1) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_COMPONENT_SUMMARY.get(d.getName(), ufpn)); |
| | | app.println(); |
| | | displayPropertyValues(app, d, currentValues); |
| | | } |
| | | |
| | | // Create a list of possible names. |
| | | final Set<String> values = new TreeSet<String>(d); |
| | | ManagedObjectPath<?, ?> path = d.getParentPath(); |
| | | try { |
| | | values.addAll(Arrays.asList(context.listManagedObjects(path, rd))); |
| | | } catch (AuthorizationException e) { |
| | | this.e = new ClientException(ReturnCode.CLIENT_SIDE_PARAM_ERROR, LocalizableMessage.raw(e |
| | | .getMessage())); |
| | | return MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | this.e = new ClientException(ReturnCode.NO_SUCH_OBJECT, e.getMessageObject()); |
| | | return MenuResult.cancel(); |
| | | } catch (ErrorResultException e) { |
| | | this.e = new ClientException(ReturnCode.APPLICATION_ERROR, LocalizableMessage.raw(e.getMessage())); |
| | | return MenuResult.quit(); |
| | | } |
| | | |
| | | // Create the add values call-back. |
| | | MenuCallback<Boolean> addCallback = null; |
| | | values.removeAll(currentValues); |
| | | if (!values.isEmpty()) { |
| | | addCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuBuilder<String> builder = new MenuBuilder<String>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_COMPONENTS_ADD.get(ufpn)); |
| | | builder.setAllowMultiSelect(true); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | for (String value : values) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(svalue, MenuResult.success(value)); |
| | | } |
| | | MenuCallback<String> callback = new CreateComponentCallback<C, S>(d); |
| | | builder.addNumberedOption( |
| | | INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT.get(rd.getUserFriendlyName()), callback); |
| | | |
| | | if (values.size() > 1) { |
| | | // No point in having this option if there's only one |
| | | // possible value. |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_ADD_ALL_COMPONENTS.get(ufpn), |
| | | MenuResult.success(values)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | |
| | | builder.addCancelOption(true); |
| | | builder.addQuitOption(); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | Menu<String> menu = builder.toMenu(); |
| | | MenuResult<String> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | // Set the new property value(s). |
| | | Collection<String> addedValues = result.getValues(); |
| | | currentValues.addAll(addedValues); |
| | | |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | |
| | | }; |
| | | } |
| | | |
| | | // Create the remove values call-back. |
| | | MenuCallback<Boolean> removeCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuBuilder<String> builder = new MenuBuilder<String>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_COMPONENTS_REMOVE.get(ufpn)); |
| | | builder.setAllowMultiSelect(true); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | for (String value : currentValues) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(svalue, MenuResult.success(value)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | |
| | | builder.addCancelOption(true); |
| | | builder.addQuitOption(); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | Menu<String> menu = builder.toMenu(); |
| | | MenuResult<String> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | // Set the new property value(s). |
| | | Collection<String> removedValues = result.getValues(); |
| | | currentValues.removeAll(removedValues); |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | |
| | | }; |
| | | |
| | | MenuResult<Boolean> result = runMenu(d, app, defaultValues, oldValues, currentValues, addCallback, |
| | | removeCallback); |
| | | if (!result.isAgain()) { |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T extends Enum<T>> MenuResult<Boolean> visitEnum(final EnumPropertyDefinition<T> d, Void p) { |
| | | final SortedSet<T> defaultValues = mo.getPropertyDefaultValues(d); |
| | | final SortedSet<T> oldValues = mo.getPropertyValues(d); |
| | | final SortedSet<T> currentValues = mo.getPropertyValues(d); |
| | | |
| | | boolean isFirst = true; |
| | | while (true) { |
| | | if (!isFirst) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT.get(d.getName())); |
| | | } else { |
| | | isFirst = false; |
| | | } |
| | | |
| | | if (currentValues.size() > 1) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_VALUES_SUMMARY.get(d.getName())); |
| | | app.println(); |
| | | displayPropertyValues(app, d, currentValues); |
| | | } |
| | | |
| | | // Create the add values call-back. |
| | | MenuCallback<Boolean> addCallback = null; |
| | | |
| | | final EnumSet<T> values = EnumSet.allOf(d.getEnumClass()); |
| | | values.removeAll(currentValues); |
| | | |
| | | if (!values.isEmpty()) { |
| | | addCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuBuilder<T> builder = new MenuBuilder<T>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_ADD.get()); |
| | | builder.setAllowMultiSelect(true); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | for (T value : values) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(svalue, MenuResult.success(value)); |
| | | } |
| | | |
| | | if (values.size() > 1) { |
| | | // No point in having this option if there's only one |
| | | // possible value. |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_ADD_ALL_VALUES.get(), |
| | | MenuResult.success(values)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | |
| | | builder.addCancelOption(true); |
| | | builder.addQuitOption(); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | Menu<T> menu = builder.toMenu(); |
| | | MenuResult<T> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | |
| | | // Set the new property value(s). |
| | | Collection<T> addedValues = result.getValues(); |
| | | currentValues.addAll(addedValues); |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | |
| | | }; |
| | | } |
| | | |
| | | // Create the remove values call-back. |
| | | MenuCallback<Boolean> removeCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuBuilder<T> builder = new MenuBuilder<T>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_REMOVE.get()); |
| | | builder.setAllowMultiSelect(true); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | for (T value : currentValues) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(svalue, MenuResult.success(value)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | |
| | | builder.addCancelOption(true); |
| | | builder.addQuitOption(); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | Menu<T> menu = builder.toMenu(); |
| | | MenuResult<T> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | // Set the new property value(s). |
| | | Collection<T> removedValues = result.getValues(); |
| | | currentValues.removeAll(removedValues); |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | |
| | | }; |
| | | |
| | | MenuResult<Boolean> result = runMenu(d, app, defaultValues, oldValues, currentValues, addCallback, |
| | | removeCallback); |
| | | if (!result.isAgain()) { |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> MenuResult<Boolean> visitUnknown(final PropertyDefinition<T> d, Void p) { |
| | | app.println(); |
| | | displayPropertySyntax(app, d); |
| | | |
| | | final SortedSet<T> defaultValues = mo.getPropertyDefaultValues(d); |
| | | final SortedSet<T> oldValues = mo.getPropertyValues(d); |
| | | final SortedSet<T> currentValues = mo.getPropertyValues(d); |
| | | |
| | | boolean isFirst = true; |
| | | while (true) { |
| | | if (!isFirst) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT.get(d.getName())); |
| | | } else { |
| | | isFirst = false; |
| | | } |
| | | |
| | | if (currentValues.size() > 1) { |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_VALUES_SUMMARY.get(d.getName())); |
| | | app.println(); |
| | | displayPropertyValues(app, d, currentValues); |
| | | } |
| | | |
| | | // Create the add values call-back. |
| | | MenuCallback<Boolean> addCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | app.println(); |
| | | SortedSet<T> previousValues = new TreeSet<T>(currentValues); |
| | | readPropertyValues(app, mo.getManagedObjectDefinition(), d, currentValues); |
| | | SortedSet<T> addedValues = new TreeSet<T>(currentValues); |
| | | addedValues.removeAll(previousValues); |
| | | isLastChoiceReset = false; |
| | | return MenuResult.success(false); |
| | | } |
| | | |
| | | }; |
| | | |
| | | // Create the remove values call-back. |
| | | MenuCallback<Boolean> removeCallback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuBuilder<T> builder = new MenuBuilder<T>(app); |
| | | |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_REMOVE.get()); |
| | | builder.setAllowMultiSelect(true); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | |
| | | for (T value : currentValues) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | builder.addNumberedOption(svalue, MenuResult.success(value)); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | |
| | | builder.addCancelOption(true); |
| | | builder.addQuitOption(); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | Menu<T> menu = builder.toMenu(); |
| | | MenuResult<T> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | // Set the new property value(s). |
| | | Collection<T> removedValues = result.getValues(); |
| | | currentValues.removeAll(removedValues); |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | |
| | | }; |
| | | |
| | | MenuResult<Boolean> result = runMenu(d, app, defaultValues, oldValues, currentValues, addCallback, |
| | | removeCallback); |
| | | if (!result.isAgain()) { |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Generate an appropriate menu option for a property which asks the user whether or not they want to keep the |
| | | * property's current settings. |
| | | */ |
| | | private <T> LocalizableMessage getKeepDefaultValuesMenuOption(PropertyDefinition<T> pd, |
| | | SortedSet<T> defaultValues, SortedSet<T> oldValues, SortedSet<T> currentValues) { |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | |
| | | boolean isModified = !currentValues.equals(oldValues); |
| | | boolean isDefault = currentValues.equals(defaultValues); |
| | | |
| | | if (isModified) { |
| | | switch (currentValues.size()) { |
| | | case 0: |
| | | if (query.isAlias()) { |
| | | return INFO_EDITOR_OPTION_USE_DEFAULT_ALIAS.get(query.getAliasDescription()); |
| | | } else if (query.isInherited()) { |
| | | if (query.getAliasDescription() != null) { |
| | | return INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS.get(query.getAliasDescription()); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS_UNDEFINED.get(); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get(); |
| | | } |
| | | case 1: |
| | | LocalizableMessage svalue = getPropertyValues(pd, currentValues); |
| | | if (isDefault) { |
| | | if (query.isInherited()) { |
| | | return INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUE.get(svalue); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_USE_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_USE_VALUE.get(svalue); |
| | | } |
| | | default: |
| | | if (isDefault) { |
| | | if (query.isInherited()) { |
| | | return INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUES.get(); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_USE_DEFAULT_VALUES.get(); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_USE_VALUES.get(); |
| | | } |
| | | } |
| | | } else { |
| | | switch (currentValues.size()) { |
| | | case 0: |
| | | if (query.isAlias()) { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_ALIAS.get(query.getAliasDescription()); |
| | | } else if (query.isInherited()) { |
| | | if (query.getAliasDescription() != null) { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS.get(query.getAliasDescription()); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_UNDEFINED.get(); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get(); |
| | | } |
| | | case 1: |
| | | LocalizableMessage svalue = getPropertyValues(pd, currentValues); |
| | | if (isDefault) { |
| | | if (query.isInherited()) { |
| | | return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUE.get(svalue); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_VALUE.get(svalue); |
| | | } |
| | | default: |
| | | if (isDefault) { |
| | | if (query.isInherited()) { |
| | | return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUES.get(); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUES.get(); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_VALUES.get(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Generate an appropriate menu option which should be used in the case where a property can be reset to its |
| | | * default behavior. |
| | | */ |
| | | private <T> LocalizableMessage getResetToDefaultValuesMenuOption(PropertyDefinition<T> pd, |
| | | SortedSet<T> defaultValues, SortedSet<T> currentValues) { |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | boolean isMandatory = pd.hasOption(PropertyOption.MANDATORY); |
| | | |
| | | if (!isMandatory && query.isAlias()) { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_ALIAS.get(query.getAliasDescription()); |
| | | } else if (query.isDefined()) { |
| | | // Only show this option if the current value is different |
| | | // to the default. |
| | | if (!currentValues.equals(defaultValues)) { |
| | | LocalizableMessage svalue = getPropertyValues(pd, defaultValues); |
| | | if (defaultValues.size() > 1) { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUES.get(svalue); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } else { |
| | | return null; |
| | | } |
| | | } else if (!isMandatory && query.isInherited()) { |
| | | if (defaultValues.isEmpty()) { |
| | | if (query.getAliasDescription() != null) { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS.get(query.getAliasDescription()); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_UNDEFINED.get(); |
| | | } |
| | | } else { |
| | | LocalizableMessage svalue = getPropertyValues(pd, defaultValues); |
| | | if (defaultValues.size() > 1) { |
| | | return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUES.get(svalue); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } |
| | | } else if (!isMandatory && query.isUndefined()) { |
| | | return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get(); |
| | | } else { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** Common menu processing. */ |
| | | private <T> MenuResult<Boolean> runMenu(final PropertyDefinition<T> d, ConsoleApplication app, |
| | | final SortedSet<T> defaultValues, final SortedSet<T> oldValues, final SortedSet<T> currentValues, |
| | | MenuCallback<Boolean> addCallback, MenuCallback<Boolean> removeCallback) { |
| | | // Construct a menu of actions. |
| | | MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app); |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName())); |
| | | |
| | | // First option is for leaving the property unchanged or |
| | | // applying changes, but only if the state of the property is |
| | | // valid. |
| | | if (!(d.hasOption(PropertyOption.MANDATORY) && currentValues.isEmpty())) { |
| | | MenuResult<Boolean> result; |
| | | if (!oldValues.equals(currentValues)) { |
| | | result = MenuResult.success(true); |
| | | } else { |
| | | result = MenuResult.<Boolean> cancel(); |
| | | } |
| | | |
| | | LocalizableMessage option = getKeepDefaultValuesMenuOption(d, defaultValues, oldValues, currentValues); |
| | | builder.addNumberedOption(option, result); |
| | | builder.setDefault(LocalizableMessage.raw("1"), result); |
| | | } |
| | | |
| | | // Add an option for adding some values. |
| | | if (addCallback != null) { |
| | | int i = builder.addNumberedOption(INFO_EDITOR_OPTION_ADD_ONE_OR_MORE_VALUES.get(), addCallback); |
| | | if (d.hasOption(PropertyOption.MANDATORY) && currentValues.isEmpty()) { |
| | | builder.setDefault(LocalizableMessage.raw("%d", i), addCallback); |
| | | } |
| | | } |
| | | |
| | | // Add options for removing values if applicable. |
| | | if (!currentValues.isEmpty()) { |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_REMOVE_ONE_OR_MORE_VALUES.get(), removeCallback); |
| | | } |
| | | |
| | | // Add options for removing all values and for resetting the |
| | | // property to its default behavior. |
| | | LocalizableMessage resetOption = null; |
| | | if (!currentValues.equals(defaultValues)) { |
| | | resetOption = getResetToDefaultValuesMenuOption(d, defaultValues, currentValues); |
| | | } |
| | | |
| | | if (!currentValues.isEmpty() && (resetOption == null || !defaultValues.isEmpty())) { |
| | | MenuCallback<Boolean> callback = new MenuCallback<Boolean>() { |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | isLastChoiceReset = false; |
| | | currentValues.clear(); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } |
| | | }; |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_REMOVE_ALL_VALUES.get(), callback); |
| | | } |
| | | |
| | | if (resetOption != null) { |
| | | MenuCallback<Boolean> callback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | currentValues.clear(); |
| | | currentValues.addAll(defaultValues); |
| | | isLastChoiceReset = true; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } |
| | | |
| | | }; |
| | | |
| | | builder.addNumberedOption(resetOption, callback); |
| | | } |
| | | |
| | | // Add an option for undoing any changes. |
| | | if (!oldValues.equals(currentValues)) { |
| | | MenuCallback<Boolean> callback = new MenuCallback<Boolean>() { |
| | | |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | currentValues.clear(); |
| | | currentValues.addAll(oldValues); |
| | | isLastChoiceReset = false; |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } |
| | | |
| | | }; |
| | | |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_REVERT_CHANGES.get(), callback); |
| | | } |
| | | |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<Boolean> menu = builder.toMenu(); |
| | | MenuResult<Boolean> result; |
| | | try { |
| | | app.println(); |
| | | result = menu.run(); |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return null; |
| | | } |
| | | |
| | | if (result.isSuccess()) { |
| | | if (result.getValue()) { |
| | | |
| | | // Set the new property value(s). |
| | | mo.setPropertyValues(d, currentValues); |
| | | |
| | | registerModification(d, currentValues, oldValues); |
| | | |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | // Continue until cancel/apply changes. |
| | | app.println(); |
| | | return MenuResult.again(); |
| | | } |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A help call-back which displays a description and summary of a single property. |
| | | */ |
| | | private static final class PropertyHelpCallback implements HelpCallback { |
| | | |
| | | /** The managed object definition. */ |
| | | private final ManagedObjectDefinition<?, ?> d; |
| | | |
| | | /** The property to be edited. */ |
| | | private final PropertyDefinition<?> pd; |
| | | |
| | | /** Creates a new property helper for the specified property. */ |
| | | private PropertyHelpCallback(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd) { |
| | | this.d = d; |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void display(ConsoleApplication app) { |
| | | app.println(); |
| | | HelpSubCommandHandler.displayVerboseSingleProperty(app, d, pd.getName()); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A menu call-back for viewing a read-only properties. |
| | | */ |
| | | private final class ReadOnlyPropertyViewer extends PropertyDefinitionVisitor<MenuResult<Boolean>, Void> implements |
| | | MenuCallback<Boolean> { |
| | | |
| | | /** Any exception that was caught during processing. */ |
| | | private ClientException e = null; |
| | | |
| | | /** The managed object being edited. */ |
| | | private final ManagedObject<?> mo; |
| | | |
| | | /** The property to be edited. */ |
| | | private final PropertyDefinition<?> pd; |
| | | |
| | | /** Creates a new property editor for the specified property. */ |
| | | private ReadOnlyPropertyViewer(ManagedObject<?> mo, PropertyDefinition<?> pd) { |
| | | this.mo = mo; |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | MenuResult<Boolean> result = pd.accept(this, null); |
| | | if (e != null) { |
| | | throw e; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> MenuResult<Boolean> visitUnknown(PropertyDefinition<T> pd, Void p) { |
| | | SortedSet<T> values = mo.getPropertyValues(pd); |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | switch (values.size()) { |
| | | case 0: |
| | | // Only alias, undefined, or inherited alias or undefined |
| | | // properties should apply here. |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | LocalizableMessage aliasDescription = query.getAliasDescription(); |
| | | if (aliasDescription == null) { |
| | | app.println(INFO_EDITOR_HEADING_READ_ONLY_ALIAS_UNDEFINED.get(pd.getName())); |
| | | } else { |
| | | app.println(INFO_EDITOR_HEADING_READ_ONLY_ALIAS.get(pd.getName(), aliasDescription)); |
| | | } |
| | | break; |
| | | case 1: |
| | | LocalizableMessage svalue = getPropertyValues(pd, mo); |
| | | app.println(INFO_EDITOR_HEADING_READ_ONLY_VALUE.get(pd.getName(), svalue)); |
| | | break; |
| | | default: |
| | | app.println(INFO_EDITOR_HEADING_READ_ONLY_VALUES.get(pd.getName())); |
| | | app.println(); |
| | | displayPropertyValues(app, pd, values); |
| | | break; |
| | | } |
| | | |
| | | app.println(); |
| | | boolean result; |
| | | try { |
| | | result = app.confirmAction(INFO_EDITOR_PROMPT_READ_ONLY.get(), false); |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return null; |
| | | } |
| | | |
| | | if (result) { |
| | | app.println(); |
| | | HelpSubCommandHandler.displayVerboseSingleProperty(app, mo.getManagedObjectDefinition(), pd.getName()); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | } |
| | | |
| | | return MenuResult.again(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A menu call-back for editing a modifiable single-valued property. |
| | | */ |
| | | private final class SingleValuedPropertyEditor extends PropertyDefinitionVisitor<MenuResult<Boolean>, Void> |
| | | implements MenuCallback<Boolean> { |
| | | |
| | | /** Any exception that was caught during processing. */ |
| | | private ClientException e = null; |
| | | |
| | | /** The managed object being edited. */ |
| | | private final ManagedObject<?> mo; |
| | | |
| | | /** The property to be edited. */ |
| | | private final PropertyDefinition<?> pd; |
| | | |
| | | /** Creates a new property editor for the specified property. */ |
| | | private SingleValuedPropertyEditor(ManagedObject<?> mo, PropertyDefinition<?> pd) { |
| | | Reject.ifFalse(!pd.hasOption(PropertyOption.MULTI_VALUED)); |
| | | |
| | | this.mo = mo; |
| | | this.pd = pd; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Boolean> invoke(ConsoleApplication app) throws ClientException { |
| | | displayPropertyHeader(app, pd); |
| | | |
| | | MenuResult<Boolean> result = pd.accept(this, null); |
| | | if (e != null) { |
| | | throw e; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <C extends ConfigurationClient, S extends Configuration> MenuResult<Boolean> visitAggregation( |
| | | AggregationPropertyDefinition<C, S> d, Void p) { |
| | | // Construct a menu of actions. |
| | | MenuBuilder<String> builder = new MenuBuilder<String>(app); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName())); |
| | | |
| | | DefaultBehaviorQuery<String> query = DefaultBehaviorQuery.query(d); |
| | | SortedSet<String> currentValues = mo.getPropertyValues(d); |
| | | SortedSet<String> defaultValues = mo.getPropertyDefaultValues(d); |
| | | String currentValue = currentValues.isEmpty() ? null : currentValues.first(); |
| | | String defaultValue = defaultValues.isEmpty() ? null : defaultValues.first(); |
| | | |
| | | // First option is for leaving the property unchanged. |
| | | LocalizableMessage option = getKeepDefaultValuesMenuOption(d); |
| | | builder.addNumberedOption(option, MenuResult.<String> cancel()); |
| | | builder.setDefault(LocalizableMessage.raw("1"), MenuResult.<String> cancel()); |
| | | |
| | | // Create a list of possible names. |
| | | final Set<String> values = new TreeSet<String>(d); |
| | | ManagedObjectPath<?, ?> path = d.getParentPath(); |
| | | InstantiableRelationDefinition<C, S> rd = d.getRelationDefinition(); |
| | | try { |
| | | values.addAll(Arrays.asList(context.listManagedObjects(path, rd))); |
| | | } catch (AuthorizationException e) { |
| | | this.e = new ClientException( |
| | | ReturnCode.CLIENT_SIDE_PARAM_ERROR, LocalizableMessage.raw(e.getMessage())); |
| | | return MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | this.e = new ClientException(ReturnCode.NO_SUCH_OBJECT, e.getMessageObject()); |
| | | return MenuResult.cancel(); |
| | | } catch (ErrorResultException e) { |
| | | this.e = new ClientException(ReturnCode.APPLICATION_ERROR, LocalizableMessage.raw(e.getMessage())); |
| | | return MenuResult.quit(); |
| | | } |
| | | |
| | | final LocalizableMessage ufn = rd.getUserFriendlyName(); |
| | | for (String value : values) { |
| | | if (currentValue != null && d.compare(value, currentValue) == 0) { |
| | | // This option is unnecessary. |
| | | continue; |
| | | } |
| | | |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | if (value.equals(defaultValue) && query.isDefined()) { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_COMPONENT.get(ufn, svalue); |
| | | } else { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_COMPONENT.get(ufn, svalue); |
| | | } |
| | | |
| | | builder.addNumberedOption(option, MenuResult.success(value)); |
| | | } |
| | | MenuCallback<String> callback = new CreateComponentCallback<C, S>(d); |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT.get(ufn), callback); |
| | | |
| | | // Third option is to reset the value back to its default. |
| | | if (mo.isPropertyPresent(d) && !query.isDefined()) { |
| | | option = getResetToDefaultValuesMenuOption(d); |
| | | if (option != null) { |
| | | builder.addNumberedOption(option, MenuResult.<String> success()); |
| | | } |
| | | } |
| | | |
| | | return runMenu(d, builder); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public MenuResult<Boolean> visitBoolean(BooleanPropertyDefinition d, Void p) { |
| | | // Construct a menu of actions. |
| | | MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app); |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName())); |
| | | |
| | | DefaultBehaviorQuery<Boolean> query = DefaultBehaviorQuery.query(d); |
| | | SortedSet<Boolean> currentValues = mo.getPropertyValues(d); |
| | | SortedSet<Boolean> defaultValues = mo.getPropertyDefaultValues(d); |
| | | |
| | | Boolean currentValue = currentValues.isEmpty() ? null : currentValues.first(); |
| | | Boolean defaultValue = defaultValues.isEmpty() ? null : defaultValues.first(); |
| | | |
| | | // First option is for leaving the property unchanged. |
| | | LocalizableMessage option = getKeepDefaultValuesMenuOption(d); |
| | | builder.addNumberedOption(option, MenuResult.<Boolean> cancel()); |
| | | builder.setDefault(LocalizableMessage.raw("1"), MenuResult.<Boolean> cancel()); |
| | | |
| | | // The second (and possibly third) option is to always change |
| | | // the property's value. |
| | | if (currentValue == null || !currentValue) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(true)); |
| | | |
| | | if (defaultValue != null && defaultValue) { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue); |
| | | } else { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue); |
| | | } |
| | | |
| | | builder.addNumberedOption(option, MenuResult.success(true)); |
| | | } |
| | | |
| | | if (currentValue == null || currentValue) { |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(false)); |
| | | |
| | | if (defaultValue != null && !defaultValue) { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue); |
| | | } else { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue); |
| | | } |
| | | |
| | | builder.addNumberedOption(option, MenuResult.success(false)); |
| | | } |
| | | |
| | | // Final option is to reset the value back to its default. |
| | | if (mo.isPropertyPresent(d) && !query.isDefined()) { |
| | | option = getResetToDefaultValuesMenuOption(d); |
| | | if (option != null) { |
| | | builder.addNumberedOption(option, MenuResult.<Boolean> success()); |
| | | } |
| | | } |
| | | |
| | | return runMenu(d, builder); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <E extends Enum<E>> MenuResult<Boolean> visitEnum(EnumPropertyDefinition<E> d, Void p) { |
| | | // Construct a menu of actions. |
| | | MenuBuilder<E> builder = new MenuBuilder<E>(app); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName())); |
| | | |
| | | DefaultBehaviorQuery<E> query = DefaultBehaviorQuery.query(d); |
| | | SortedSet<E> currentValues = mo.getPropertyValues(d); |
| | | SortedSet<E> defaultValues = mo.getPropertyDefaultValues(d); |
| | | E currentValue = currentValues.isEmpty() ? null : currentValues.first(); |
| | | E defaultValue = defaultValues.isEmpty() ? null : defaultValues.first(); |
| | | |
| | | // First option is for leaving the property unchanged. |
| | | LocalizableMessage option = getKeepDefaultValuesMenuOption(d); |
| | | builder.addNumberedOption(option, MenuResult.<E> cancel()); |
| | | builder.setDefault(LocalizableMessage.raw("1"), MenuResult.<E> cancel()); |
| | | |
| | | // Create options for changing to other values. |
| | | Set<E> values = new TreeSet<E>(d); |
| | | values.addAll(EnumSet.allOf(d.getEnumClass())); |
| | | for (E value : values) { |
| | | if (value.equals(currentValue) && query.isDefined()) { |
| | | // This option is unnecessary. |
| | | continue; |
| | | } |
| | | |
| | | LocalizableMessage svalue = getPropertyValues(d, Collections.singleton(value)); |
| | | |
| | | if (value.equals(defaultValue) && query.isDefined()) { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue); |
| | | } else { |
| | | option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue); |
| | | } |
| | | |
| | | builder.addNumberedOption(option, MenuResult.success(value)); |
| | | } |
| | | |
| | | // Third option is to reset the value back to its default. |
| | | if (mo.isPropertyPresent(d) && !query.isDefined()) { |
| | | option = getResetToDefaultValuesMenuOption(d); |
| | | if (option != null) { |
| | | builder.addNumberedOption(option, MenuResult.<E> success()); |
| | | } |
| | | } |
| | | |
| | | return runMenu(d, builder); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> MenuResult<Boolean> visitUnknown(final PropertyDefinition<T> d, Void p) { |
| | | app.println(); |
| | | displayPropertySyntax(app, d); |
| | | |
| | | // Construct a menu of actions. |
| | | MenuBuilder<T> builder = new MenuBuilder<T>(app); |
| | | builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName())); |
| | | |
| | | // First option is for leaving the property unchanged. |
| | | LocalizableMessage option = getKeepDefaultValuesMenuOption(d); |
| | | builder.addNumberedOption(option, MenuResult.<T> cancel()); |
| | | builder.setDefault(LocalizableMessage.raw("1"), MenuResult.<T> cancel()); |
| | | |
| | | // The second option is to always change the property's value. |
| | | builder.addNumberedOption(INFO_EDITOR_OPTION_CHANGE_VALUE.get(), new MenuCallback<T>() { |
| | | |
| | | @Override |
| | | public MenuResult<T> invoke(ConsoleApplication app) throws ClientException { |
| | | app.println(); |
| | | Set<T> values = readPropertyValues(app, mo.getManagedObjectDefinition(), d); |
| | | return MenuResult.success(values); |
| | | } |
| | | |
| | | }); |
| | | |
| | | // Third option is to reset the value back to its default. |
| | | if (mo.isPropertyPresent(d)) { |
| | | option = getResetToDefaultValuesMenuOption(d); |
| | | if (option != null) { |
| | | builder.addNumberedOption(option, MenuResult.<T> success()); |
| | | } |
| | | } |
| | | |
| | | return runMenu(d, builder); |
| | | } |
| | | |
| | | /** |
| | | * Generate an appropriate menu option for a property which asks the user whether or not they want to keep the |
| | | * property's current settings. |
| | | */ |
| | | private <T> LocalizableMessage getKeepDefaultValuesMenuOption(PropertyDefinition<T> pd) { |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | SortedSet<T> currentValues = mo.getPropertyValues(pd); |
| | | SortedSet<T> defaultValues = mo.getPropertyDefaultValues(pd); |
| | | |
| | | if (query.isDefined() && currentValues.equals(defaultValues)) { |
| | | LocalizableMessage svalue = getPropertyValues(pd, currentValues); |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUE.get(svalue); |
| | | } else if (mo.isPropertyPresent(pd)) { |
| | | LocalizableMessage svalue = getPropertyValues(pd, currentValues); |
| | | return INFO_EDITOR_OPTION_KEEP_VALUE.get(svalue); |
| | | } else if (query.isAlias()) { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_ALIAS.get(query.getAliasDescription()); |
| | | } else if (query.isInherited()) { |
| | | if (defaultValues.isEmpty()) { |
| | | if (query.getAliasDescription() != null) { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS.get(query.getAliasDescription()); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_UNDEFINED.get(); |
| | | } |
| | | } else { |
| | | LocalizableMessage svalue = getPropertyValues(pd, defaultValues); |
| | | return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } else { |
| | | return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Generate an appropriate menu option which should be used in the case where a property can be reset to its |
| | | * default behavior. |
| | | */ |
| | | private <T> LocalizableMessage getResetToDefaultValuesMenuOption(PropertyDefinition<T> pd) { |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | SortedSet<T> currentValues = mo.getPropertyValues(pd); |
| | | SortedSet<T> defaultValues = mo.getPropertyDefaultValues(pd); |
| | | |
| | | boolean isMandatory = pd.hasOption(PropertyOption.MANDATORY); |
| | | |
| | | if (!isMandatory && query.isAlias()) { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_ALIAS.get(query.getAliasDescription()); |
| | | } else if (query.isDefined()) { |
| | | // Only show this option if the current value is different |
| | | // to the default. |
| | | if (!currentValues.equals(defaultValues)) { |
| | | LocalizableMessage svalue = getPropertyValues(pd, defaultValues); |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | return null; |
| | | } else if (!isMandatory && query.isInherited()) { |
| | | if (defaultValues.isEmpty()) { |
| | | if (query.getAliasDescription() != null) { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS.get(query.getAliasDescription()); |
| | | } else { |
| | | return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_UNDEFINED.get(); |
| | | } |
| | | } else { |
| | | LocalizableMessage svalue = getPropertyValues(pd, defaultValues); |
| | | return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUE.get(svalue); |
| | | } |
| | | } else if (!isMandatory && query.isUndefined()) { |
| | | return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** Common menu processing. */ |
| | | private <T> MenuResult<Boolean> runMenu(final PropertyDefinition<T> d, MenuBuilder<T> builder) { |
| | | builder.addHelpOption(new PropertyHelpCallback(mo.getManagedObjectDefinition(), d)); |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<T> menu = builder.toMenu(); |
| | | MenuResult<T> result; |
| | | try { |
| | | app.println(); |
| | | result = menu.run(); |
| | | } catch (ClientException e) { |
| | | this.e = e; |
| | | return null; |
| | | } |
| | | |
| | | if (result.isSuccess()) { |
| | | // Set the new property value(s). |
| | | Collection<T> newValues = result.getValues(); |
| | | SortedSet<T> oldValues = new TreeSet<T>(mo.getPropertyValues(d)); |
| | | mo.setPropertyValues(d, newValues); |
| | | |
| | | // If there are no newValues when we do a reset. |
| | | isLastChoiceReset = newValues.isEmpty(); |
| | | registerModification(d, new TreeSet<T>(newValues), oldValues); |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else if (result.isCancel()) { |
| | | app.println(); |
| | | app.pressReturnToContinue(); |
| | | return MenuResult.success(false); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** Display a title and a description of the property. */ |
| | | private static void displayPropertyHeader(ConsoleApplication app, PropertyDefinition<?> pd) { |
| | | app.println(); |
| | | app.println(); |
| | | app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY.get(pd.getName())); |
| | | app.println(); |
| | | app.errPrintln(pd.getSynopsis(), 4); |
| | | if (pd.getDescription() != null) { |
| | | app.println(); |
| | | app.errPrintln(pd.getDescription(), 4); |
| | | } |
| | | } |
| | | |
| | | /** Display a property's syntax. */ |
| | | private static <T> void displayPropertySyntax(ConsoleApplication app, PropertyDefinition<T> d) { |
| | | PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true); |
| | | |
| | | TableBuilder builder = new TableBuilder(); |
| | | builder.startRow(); |
| | | builder.appendCell(INFO_EDITOR_HEADING_SYNTAX.get()); |
| | | builder.appendCell(b.getUsage(d)); |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setIndentWidth(4); |
| | | printer.setColumnWidth(1, 0); |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** Display a table of property values. */ |
| | | private static <T> void displayPropertyValues(ConsoleApplication app, PropertyDefinition<T> pd, |
| | | Collection<T> values) { |
| | | TableBuilder builder = new TableBuilder(); |
| | | PropertyValuePrinter valuePrinter = new PropertyValuePrinter(null, null, false); |
| | | |
| | | int sz = values.size(); |
| | | boolean useMultipleColumns = (sz >= MULTI_COLUMN_THRESHOLD); |
| | | int rows = sz; |
| | | if (useMultipleColumns) { |
| | | // Display in two columns the first column should contain |
| | | // half the values. If there are an odd number of columns |
| | | // then the first column should contain an additional value |
| | | // (e.g. if there are 23 values, the first column should |
| | | // contain 12 values and the second column 11 values). |
| | | rows /= 2; |
| | | rows += sz % 2; |
| | | } |
| | | |
| | | List<T> vl = new ArrayList<T>(values); |
| | | for (int i = 0, j = rows; i < rows; i++, j++) { |
| | | builder.startRow(); |
| | | builder.appendCell("*)"); |
| | | builder.appendCell(valuePrinter.print(pd, vl.get(i))); |
| | | |
| | | if (useMultipleColumns && (j < sz)) { |
| | | builder.appendCell(); |
| | | builder.appendCell("*)"); |
| | | builder.appendCell(valuePrinter.print(pd, vl.get(j))); |
| | | } |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(app.getErrorStream()); |
| | | printer.setDisplayHeadings(false); |
| | | printer.setIndentWidth(4); |
| | | printer.setColumnWidth(1, 0); |
| | | if (useMultipleColumns) { |
| | | printer.setColumnWidth(2, 2); |
| | | printer.setColumnWidth(4, 0); |
| | | } |
| | | builder.print(printer); |
| | | } |
| | | |
| | | /** Display the set of values associated with a property. */ |
| | | private static <T> LocalizableMessage getPropertyValues(PropertyDefinition<T> pd, Collection<T> values) { |
| | | if (values.isEmpty()) { |
| | | // There are no values or default values. Display the default |
| | | // behavior for alias values. |
| | | DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd); |
| | | LocalizableMessage content = query.getAliasDescription(); |
| | | if (content != null) { |
| | | return content; |
| | | } |
| | | return LocalizableMessage.raw("-"); |
| | | } else { |
| | | PropertyValuePrinter printer = new PropertyValuePrinter(null, null, false); |
| | | LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); |
| | | |
| | | boolean isFirst = true; |
| | | for (T value : values) { |
| | | if (!isFirst) { |
| | | builder.append(", "); |
| | | } |
| | | builder.append(printer.print(pd, value)); |
| | | isFirst = false; |
| | | } |
| | | |
| | | return builder.toMessage(); |
| | | } |
| | | } |
| | | |
| | | /** Display the set of values associated with a property. */ |
| | | private static <T> LocalizableMessage getPropertyValues(PropertyDefinition<T> pd, ManagedObject<?> mo) { |
| | | SortedSet<T> values = mo.getPropertyValues(pd); |
| | | return getPropertyValues(pd, values); |
| | | } |
| | | |
| | | /** Read new values for a property. */ |
| | | private static <T> SortedSet<T> readPropertyValues(ConsoleApplication app, ManagedObjectDefinition<?, ?> d, |
| | | PropertyDefinition<T> pd) throws ClientException { |
| | | SortedSet<T> values = new TreeSet<T>(pd); |
| | | readPropertyValues(app, d, pd, values); |
| | | return values; |
| | | } |
| | | |
| | | /** Add values to a property. */ |
| | | private static <T> void readPropertyValues(ConsoleApplication app, ManagedObjectDefinition<?, ?> d, |
| | | PropertyDefinition<T> pd, SortedSet<T> values) throws ClientException { |
| | | // Make sure there is at least one value if mandatory and empty. |
| | | if (values.isEmpty()) { |
| | | while (true) { |
| | | try { |
| | | LocalizableMessage prompt; |
| | | |
| | | if (pd.hasOption(PropertyOption.MANDATORY)) { |
| | | prompt = INFO_EDITOR_PROMPT_READ_FIRST_VALUE.get(pd.getName()); |
| | | } else { |
| | | prompt = INFO_EDITOR_PROMPT_READ_FIRST_VALUE_OPTIONAL.get(pd.getName()); |
| | | } |
| | | |
| | | app.println(); |
| | | String s = app.readLineOfInput(prompt); |
| | | if (s.trim().length() == 0 && !pd.hasOption(PropertyOption.MANDATORY)) { |
| | | return; |
| | | } |
| | | |
| | | T value = pd.decodeValue(s); |
| | | if (values.contains(value)) { |
| | | // Prevent addition of duplicates. |
| | | app.println(); |
| | | app.println(ERR_EDITOR_READ_FIRST_DUPLICATE.get(s)); |
| | | } else { |
| | | values.add(value); |
| | | } |
| | | |
| | | break; |
| | | } catch (PropertyException e) { |
| | | app.println(); |
| | | app.println(ArgumentExceptionFactory.adaptPropertyException(e, d).getMessageObject()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (pd.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | // Prompt for more values if multi-valued. |
| | | while (true) { |
| | | try { |
| | | LocalizableMessage prompt = INFO_EDITOR_PROMPT_READ_NEXT_VALUE.get(pd.getName()); |
| | | |
| | | app.println(); |
| | | String s = app.readLineOfInput(prompt); |
| | | if (s.trim().length() == 0) { |
| | | return; |
| | | } |
| | | |
| | | T value = pd.decodeValue(s); |
| | | if (values.contains(value)) { |
| | | // Prevent addition of duplicates. |
| | | app.println(); |
| | | app.println(ERR_EDITOR_READ_NEXT_DUPLICATE.get(s)); |
| | | } else { |
| | | values.add(value); |
| | | } |
| | | } catch (PropertyException e) { |
| | | app.println(); |
| | | app.println(ArgumentExceptionFactory.adaptPropertyException(e, d).getMessageObject()); |
| | | app.println(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * The threshold above which choice menus should be displayed in multiple columns. |
| | | */ |
| | | private static final int MULTI_COLUMN_THRESHOLD = 8; |
| | | |
| | | /** The application console. */ |
| | | private final ConsoleApplication app; |
| | | |
| | | /** The management context. */ |
| | | private final ManagementContext context; |
| | | |
| | | /** |
| | | * The modifications performed: we assume that at most there is one modification per property definition. |
| | | */ |
| | | private final List<PropertyEditorModification<?>> mods = new ArrayList<PropertyEditorModification<?>>(); |
| | | |
| | | /** |
| | | * Whether the last type of choice made by the user in a menu is a reset. |
| | | */ |
| | | private boolean isLastChoiceReset; |
| | | |
| | | /** |
| | | * Create a new property value editor which will read from the provided application console. |
| | | * |
| | | * @param app |
| | | * The application console. |
| | | * @param context |
| | | * The management context. |
| | | */ |
| | | public PropertyValueEditor(ConsoleApplication app, ManagementContext context) { |
| | | this.app = app; |
| | | this.context = context; |
| | | } |
| | | |
| | | /** |
| | | * Interactively edits the properties of a managed object. Only the properties listed in the provided collection |
| | | * will be accessible to the client. It is up to the caller to ensure that the list of properties does not include |
| | | * read-only, monitoring, hidden, or advanced properties as appropriate. |
| | | * |
| | | * @param mo |
| | | * The managed object. |
| | | * @param c |
| | | * The collection of properties which can be edited. |
| | | * @param isCreate |
| | | * Flag indicating whether or not the managed object is being created. If it is then read-only properties |
| | | * will be modifiable. |
| | | * @return Returns {@code MenuResult.success()} if the changes made to the managed object should be applied, or |
| | | * {@code MenuResult.cancel()} if the user to chose to cancel any changes, or {@code MenuResult.quit()} if |
| | | * the user chose to quit the application. |
| | | * @throws ClientException |
| | | * If the user input could not be retrieved for some reason. |
| | | */ |
| | | public MenuResult<Void> edit(ManagedObject<?> mo, Collection<PropertyDefinition<?>> c, boolean isCreate) |
| | | throws ClientException { |
| | | |
| | | // Get values for this missing mandatory property. |
| | | for (PropertyDefinition<?> pd : c) { |
| | | if (pd.hasOption(PropertyOption.MANDATORY) && mo.getPropertyValues(pd).isEmpty()) { |
| | | MandatoryPropertyInitializer mpi = new MandatoryPropertyInitializer(mo, pd); |
| | | MenuResult<Void> result = mpi.invoke(app); |
| | | if (!result.isSuccess()) { |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | while (true) { |
| | | // Construct the main menu. |
| | | MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app); |
| | | |
| | | String ufn = mo.getManagedObjectPath().getName(); |
| | | if (ufn == null) { |
| | | ufn = mo.getManagedObjectDefinition().getUserFriendlyName().toString(); |
| | | } |
| | | builder.setPrompt(INFO_EDITOR_HEADING_CONFIGURE_COMPONENT.get(ufn)); |
| | | |
| | | LocalizableMessage heading1 = INFO_DSCFG_HEADING_PROPERTY_NAME.get(); |
| | | LocalizableMessage heading2 = INFO_DSCFG_HEADING_PROPERTY_VALUE.get(); |
| | | builder.setColumnHeadings(heading1, heading2); |
| | | builder.setColumnWidths(null, 0); |
| | | |
| | | // Create an option for editing/viewing each property. |
| | | for (PropertyDefinition<?> pd : c) { |
| | | // Determine whether this property should be modifiable. |
| | | boolean isReadOnly = false; |
| | | |
| | | if (pd.hasOption(PropertyOption.MONITORING)) { |
| | | isReadOnly = true; |
| | | } |
| | | |
| | | if (!isCreate && pd.hasOption(PropertyOption.READ_ONLY)) { |
| | | isReadOnly = true; |
| | | } |
| | | |
| | | // Create the appropriate property action. |
| | | MenuCallback<Boolean> callback; |
| | | if (pd.hasOption(PropertyOption.MULTI_VALUED)) { |
| | | if (isReadOnly) { |
| | | callback = new ReadOnlyPropertyViewer(mo, pd); |
| | | } else { |
| | | callback = new MultiValuedPropertyEditor(mo, pd); |
| | | } |
| | | } else { |
| | | if (isReadOnly) { |
| | | callback = new ReadOnlyPropertyViewer(mo, pd); |
| | | } else { |
| | | callback = new SingleValuedPropertyEditor(mo, pd); |
| | | } |
| | | } |
| | | |
| | | // Create the numeric option. |
| | | LocalizableMessage values = getPropertyValues(pd, mo); |
| | | builder.addNumberedOption(LocalizableMessage.raw("%s", pd.getName()), callback, values); |
| | | } |
| | | |
| | | // Add a help option which displays a summary of the managed |
| | | // object's definition. |
| | | HelpCallback helpCallback = new ComponentHelpCallback(mo, c); |
| | | builder.addHelpOption(helpCallback); |
| | | |
| | | // Add an option to apply the changes. |
| | | if (isCreate) { |
| | | builder.addCharOption(INFO_EDITOR_OPTION_FINISH_KEY.get(), |
| | | INFO_EDITOR_OPTION_FINISH_CREATE_COMPONENT.get(ufn), MenuResult.success(true)); |
| | | } else { |
| | | builder.addCharOption(INFO_EDITOR_OPTION_FINISH_KEY.get(), |
| | | INFO_EDITOR_OPTION_FINISH_MODIFY_COMPONENT.get(ufn), MenuResult.success(true)); |
| | | } |
| | | |
| | | builder.setDefault(INFO_EDITOR_OPTION_FINISH_KEY.get(), MenuResult.success(true)); |
| | | |
| | | // Add options for canceling and quitting. |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(false); |
| | | } |
| | | builder.addQuitOption(); |
| | | |
| | | // Run the menu - success indicates that any changes should be |
| | | // committed. |
| | | app.println(); |
| | | app.println(); |
| | | Menu<Boolean> menu = builder.toMenu(); |
| | | MenuResult<Boolean> result = menu.run(); |
| | | |
| | | if (result.isSuccess()) { |
| | | if (result.getValue()) { |
| | | return MenuResult.<Void> success(); |
| | | } |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | return MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Register the modification in the list of modifications. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @param newValues |
| | | * the resulting values of the property once the modification is applied. |
| | | * @param previousValues |
| | | * the values we had before the modification is applied (these are not necessarily the *original* values |
| | | * if we already have other modifications applied to the same property). |
| | | */ |
| | | private <T> void registerModification(PropertyDefinition<T> pd, SortedSet<T> newValues, |
| | | SortedSet<T> previousValues) { |
| | | |
| | | if (isLastChoiceReset) { |
| | | registerResetModification(pd, previousValues); |
| | | } else if (!newValues.equals(previousValues)) { |
| | | if (newValues.containsAll(previousValues)) { |
| | | if (newValues.size() <= 1) { |
| | | registerSetModification(pd, newValues, previousValues); |
| | | } else { |
| | | registerAddModification(pd, newValues, previousValues); |
| | | } |
| | | } else if (previousValues.containsAll(newValues)) { |
| | | registerRemoveModification(pd, newValues, previousValues); |
| | | } else if (newValues.size() <= 1) { |
| | | registerSetModification(pd, newValues, previousValues); |
| | | } else { |
| | | // Split into two operations: remove and add |
| | | SortedSet<T> removedValues = new TreeSet<T>(); |
| | | removedValues.addAll(previousValues); |
| | | removedValues.removeAll(newValues); |
| | | |
| | | PropertyEditorModification<T> removeMod = PropertyEditorModification.createRemoveModification(pd, |
| | | removedValues, previousValues); |
| | | addModification(removeMod); |
| | | |
| | | SortedSet<T> retainedValues = new TreeSet<T>(); |
| | | retainedValues.addAll(previousValues); |
| | | retainedValues.retainAll(newValues); |
| | | |
| | | SortedSet<T> addedValues = new TreeSet<T>(); |
| | | addedValues.addAll(newValues); |
| | | addedValues.removeAll(retainedValues); |
| | | |
| | | PropertyEditorModification<T> addMod = PropertyEditorModification.createAddModification(pd, |
| | | addedValues, retainedValues); |
| | | addModification(addMod); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Register a reset modification in the list of modifications. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @param previousValues |
| | | * the values we had before the modification is applied (these are not necessarily the *original* values |
| | | * if we already have other modifications applied to the same property). |
| | | */ |
| | | private <T> void registerResetModification(PropertyDefinition<T> pd, SortedSet<T> previousValues) { |
| | | PropertyEditorModification<?> mod = getModification(pd); |
| | | SortedSet<T> originalValues; |
| | | if (mod != null) { |
| | | originalValues = new TreeSet<T>(pd); |
| | | castAndAddValues(originalValues, mod.getOriginalValues(), pd); |
| | | removeModification(mod); |
| | | } else { |
| | | originalValues = new TreeSet<T>(previousValues); |
| | | } |
| | | |
| | | addModification(PropertyEditorModification.createResetModification(pd, originalValues)); |
| | | } |
| | | |
| | | /** |
| | | * Register a set modification in the list of modifications. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @param newValues |
| | | * the resulting values of the property once the modification is applied. |
| | | * @param previousValues |
| | | * the values we had before the modification is applied (these are not necessarily the *original* values |
| | | * if we already have other modifications applied to the same property). |
| | | */ |
| | | private <T> void registerSetModification(PropertyDefinition<T> pd, SortedSet<T> newValues, |
| | | SortedSet<T> previousValues) { |
| | | PropertyEditorModification<?> mod = getModification(pd); |
| | | SortedSet<T> originalValues; |
| | | if (mod != null) { |
| | | originalValues = new TreeSet<T>(pd); |
| | | castAndAddValues(originalValues, mod.getOriginalValues(), pd); |
| | | removeModification(mod); |
| | | } else { |
| | | originalValues = new TreeSet<T>(previousValues); |
| | | } |
| | | addModification(PropertyEditorModification.createSetModification(pd, newValues, originalValues)); |
| | | } |
| | | |
| | | /** |
| | | * Register an add modification in the list of modifications. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @param newValues |
| | | * the resulting values of the property once the modification is applied. |
| | | * @param previousValues |
| | | * the values we had before the modification is applied (these are not necessarily the *original* values |
| | | * if we already have other modifications applied to the same property). |
| | | */ |
| | | private <T> void registerAddModification(PropertyDefinition<T> pd, SortedSet<T> newValues, |
| | | SortedSet<T> previousValues) { |
| | | PropertyEditorModification<?> mod = getModification(pd); |
| | | PropertyEditorModification<T> newMod; |
| | | SortedSet<T> originalValues; |
| | | if (mod != null) { |
| | | originalValues = new TreeSet<T>(pd); |
| | | castAndAddValues(originalValues, mod.getOriginalValues(), pd); |
| | | if (mod.getType() == PropertyEditorModification.Type.ADD) { |
| | | SortedSet<T> addedValues = new TreeSet<T>(newValues); |
| | | addedValues.removeAll(originalValues); |
| | | newMod = PropertyEditorModification.createAddModification(pd, addedValues, originalValues); |
| | | } else { |
| | | newMod = PropertyEditorModification |
| | | .createSetModification(pd, new TreeSet<T>(newValues), originalValues); |
| | | } |
| | | removeModification(mod); |
| | | } else { |
| | | originalValues = new TreeSet<T>(previousValues); |
| | | SortedSet<T> addedValues = new TreeSet<T>(newValues); |
| | | addedValues.removeAll(originalValues); |
| | | newMod = PropertyEditorModification.createAddModification(pd, addedValues, originalValues); |
| | | } |
| | | addModification(newMod); |
| | | } |
| | | |
| | | /** |
| | | * Register a remove modification in the list of modifications. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @param newValues |
| | | * the resulting values of the property once the modification is applied. |
| | | * @param previousValues |
| | | * the values we had before the modification is applied (these are not necessarily the *original* values |
| | | * if we already have other modifications applied to the same property). |
| | | */ |
| | | private <T> void registerRemoveModification(PropertyDefinition<T> pd, SortedSet<T> newValues, |
| | | SortedSet<T> previousValues) { |
| | | PropertyEditorModification<?> mod = getModification(pd); |
| | | PropertyEditorModification<T> newMod; |
| | | SortedSet<T> originalValues; |
| | | if (mod != null) { |
| | | originalValues = new TreeSet<T>(pd); |
| | | castAndAddValues(originalValues, mod.getOriginalValues(), pd); |
| | | if (newValues.isEmpty()) { |
| | | newMod = PropertyEditorModification.createRemoveModification(pd, originalValues, originalValues); |
| | | } else if (mod.getType() == PropertyEditorModification.Type.REMOVE) { |
| | | SortedSet<T> removedValues = new TreeSet<T>(originalValues); |
| | | removedValues.removeAll(newValues); |
| | | newMod = PropertyEditorModification.createRemoveModification(pd, removedValues, originalValues); |
| | | } else { |
| | | newMod = PropertyEditorModification |
| | | .createSetModification(pd, new TreeSet<T>(newValues), originalValues); |
| | | } |
| | | removeModification(mod); |
| | | } else { |
| | | originalValues = new TreeSet<T>(previousValues); |
| | | SortedSet<T> removedValues = new TreeSet<T>(originalValues); |
| | | removedValues.removeAll(newValues); |
| | | newMod = PropertyEditorModification.createRemoveModification(pd, removedValues, originalValues); |
| | | } |
| | | addModification(newMod); |
| | | } |
| | | |
| | | /** |
| | | * Returns the modifications that have been applied during the last call of the method PropertyValueEditor.edit. |
| | | * |
| | | * @return the modifications that have been applied during the last call of the method PropertyValueEditor.edit. |
| | | */ |
| | | public Collection<PropertyEditorModification<?>> getModifications() { |
| | | return mods; |
| | | } |
| | | |
| | | /** |
| | | * Clears the list of modifications. |
| | | */ |
| | | public void resetModifications() { |
| | | mods.clear(); |
| | | } |
| | | |
| | | /** |
| | | * Adds a modification to the list of modifications that have been performed. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param mod |
| | | * the modification to be added. |
| | | */ |
| | | private <T> void addModification(PropertyEditorModification<T> mod) { |
| | | mods.add(mod); |
| | | } |
| | | |
| | | /** |
| | | * Removes a modification from the list of modifications that have been performed. |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param mod |
| | | * the modification to be removed. |
| | | */ |
| | | private <T> boolean removeModification(PropertyEditorModification<T> mod) { |
| | | return mods.remove(mod); |
| | | } |
| | | |
| | | /** |
| | | * Returns the modification associated with a given property definition: we assume that we have only one |
| | | * modification per property definition (in the worst case we merge the modifications and generate a unique set |
| | | * modification). |
| | | * |
| | | * @param <T> |
| | | * The type of the underlying property associated with the modification. |
| | | * @param pd |
| | | * the property definition. |
| | | * @return the modification associated with the provided property definition and <CODE>null</CODE> if no |
| | | * modification could be found. |
| | | */ |
| | | private <T> PropertyEditorModification<?> getModification(PropertyDefinition<T> pd) { |
| | | PropertyEditorModification<?> mod = null; |
| | | |
| | | for (PropertyEditorModification<?> m : mods) { |
| | | if (pd.equals(m.getPropertyDefinition())) { |
| | | mod = m; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return mod; |
| | | } |
| | | |
| | | /** |
| | | * This method is required to avoid compilation warnings. It basically adds the contents of a collection to another |
| | | * collection by explicitly casting its values. This is done because the method getModification() returns au |
| | | * undefined type. |
| | | * |
| | | * @param <T> |
| | | * The type of the destination values. |
| | | * @param destination |
| | | * the collection that we want to update. |
| | | * @param source |
| | | * the collection whose values we want to add (and cast) to the source collection. |
| | | * @param pd |
| | | * the PropertyDefinition we use to do the casting. |
| | | * @throws ClassCastException |
| | | * if an error occurs during the cast of the objects. |
| | | */ |
| | | private <T> void castAndAddValues(Collection<T> destination, Collection<?> source, PropertyDefinition<T> pd) { |
| | | for (Object o : source) { |
| | | destination.add(pd.castValue(o)); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | |
| | | import java.text.NumberFormat; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.config.BooleanPropertyDefinition; |
| | | import org.forgerock.opendj.config.DurationPropertyDefinition; |
| | | import org.forgerock.opendj.config.DurationUnit; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyValueVisitor; |
| | | import org.forgerock.opendj.config.SizePropertyDefinition; |
| | | import org.forgerock.opendj.config.SizeUnit; |
| | | |
| | | /** |
| | | * A class responsible for displaying property values. This class takes care of using locale specific formatting rules. |
| | | */ |
| | | final class PropertyValuePrinter { |
| | | |
| | | /** |
| | | * Perform property type specific print formatting. |
| | | */ |
| | | private static final class MyPropertyValueVisitor extends PropertyValueVisitor<LocalizableMessage, Void> { |
| | | |
| | | /** |
| | | * The requested size unit (null if the property's unit should be used). |
| | | */ |
| | | private final SizeUnit sizeUnit; |
| | | |
| | | /** |
| | | * The requested time unit (null if the property's unit should be used). |
| | | */ |
| | | private final DurationUnit timeUnit; |
| | | |
| | | /** |
| | | * Whether or not values should be displayed in a script-friendly manner. |
| | | */ |
| | | private final boolean isScriptFriendly; |
| | | |
| | | /** The formatter to use for numeric values. */ |
| | | private final NumberFormat numberFormat; |
| | | |
| | | /** Private constructor. */ |
| | | private MyPropertyValueVisitor(SizeUnit sizeUnit, DurationUnit timeUnit, boolean isScriptFriendly) { |
| | | this.sizeUnit = sizeUnit; |
| | | this.timeUnit = timeUnit; |
| | | this.isScriptFriendly = isScriptFriendly; |
| | | |
| | | numberFormat = NumberFormat.getNumberInstance(); |
| | | numberFormat.setGroupingUsed(!this.isScriptFriendly); |
| | | numberFormat.setMaximumFractionDigits(2); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public LocalizableMessage visitBoolean(BooleanPropertyDefinition pd, Boolean v, Void p) { |
| | | return v ? INFO_VALUE_TRUE.get() : INFO_VALUE_FALSE.get(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public LocalizableMessage visitDuration(DurationPropertyDefinition pd, Long v, Void p) { |
| | | if (pd.getUpperLimit() == null && (v < 0 || v == Long.MAX_VALUE)) { |
| | | return INFO_VALUE_UNLIMITED.get(); |
| | | } |
| | | |
| | | LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); |
| | | long ms = pd.getBaseUnit().toMilliSeconds(v); |
| | | |
| | | if (timeUnit == null && !isScriptFriendly && ms != 0) { |
| | | // Use human-readable string representation by default. |
| | | builder.append(DurationUnit.toString(ms)); |
| | | } else { |
| | | // Use either the specified unit or the property definition's |
| | | // base unit. |
| | | DurationUnit unit = timeUnit; |
| | | if (unit == null) { |
| | | unit = pd.getBaseUnit(); |
| | | } |
| | | |
| | | builder.append(numberFormat.format(unit.fromMilliSeconds(ms))); |
| | | builder.append(' '); |
| | | builder.append(unit.getShortName()); |
| | | } |
| | | |
| | | return builder.toMessage(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public LocalizableMessage visitSize(SizePropertyDefinition pd, Long v, Void p) { |
| | | if (pd.isAllowUnlimited() && v < 0) { |
| | | return INFO_VALUE_UNLIMITED.get(); |
| | | } |
| | | |
| | | SizeUnit unit = sizeUnit; |
| | | if (unit == null) { |
| | | if (isScriptFriendly) { |
| | | // Assume users want a more accurate value. |
| | | unit = SizeUnit.getBestFitUnitExact(v); |
| | | } else { |
| | | unit = SizeUnit.getBestFitUnit(v); |
| | | } |
| | | } |
| | | |
| | | LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); |
| | | builder.append(numberFormat.format(unit.fromBytes(v))); |
| | | builder.append(' '); |
| | | builder.append(unit.getShortName()); |
| | | |
| | | return builder.toMessage(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <T> LocalizableMessage visitUnknown(PropertyDefinition<T> pd, T v, Void p) { |
| | | // For all other property definition types the default encoding |
| | | // will do. |
| | | String s = pd.encodeValue(v); |
| | | if (isScriptFriendly) { |
| | | return LocalizableMessage.raw("%s", s); |
| | | } else if (s.trim().length() == 0 || s.contains(",")) { |
| | | // Quote empty strings or strings containing commas |
| | | // non-scripting mode. |
| | | return LocalizableMessage.raw("\"%s\"", s); |
| | | } else { |
| | | return LocalizableMessage.raw("%s", s); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** The property value printer implementation. */ |
| | | private final MyPropertyValueVisitor pimpl; |
| | | |
| | | /** |
| | | * Creates a new property value printer. |
| | | * |
| | | * @param sizeUnit |
| | | * The user requested size unit or <code>null</code> if best-fit. |
| | | * @param timeUnit |
| | | * The user requested time unit or <code>null</code> if best-fit. |
| | | * @param isScriptFriendly |
| | | * If values should be displayed in a script friendly manner. |
| | | */ |
| | | public PropertyValuePrinter(SizeUnit sizeUnit, DurationUnit timeUnit, boolean isScriptFriendly) { |
| | | this.pimpl = new MyPropertyValueVisitor(sizeUnit, timeUnit, isScriptFriendly); |
| | | } |
| | | |
| | | /** |
| | | * Print a property value according to the rules of this property value printer. |
| | | * |
| | | * @param <T> |
| | | * The type of property value. |
| | | * @param pd |
| | | * The property definition. |
| | | * @param value |
| | | * The property value. |
| | | * @return Returns the string representation of the property value encoded according to the rules of this property |
| | | * value printer. |
| | | */ |
| | | public <T> LocalizableMessage print(PropertyDefinition<T> pd, T value) { |
| | | return pd.accept(pimpl, value, null); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2011-2014 ForgeRock AS |
| | | * Portions Copyright 2012 profiq, s.r.o. |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayMissingMandatoryPropertyException; |
| | | import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.displayOperationRejectedException; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.AggregationPropertyDefinition; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectAlreadyExistsException; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyException; |
| | | import org.forgerock.opendj.config.PropertyOption; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.SingletonRelationDefinition; |
| | | import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException; |
| | | import org.forgerock.opendj.config.client.OperationRejectedException; |
| | | import org.forgerock.opendj.config.conditions.Condition; |
| | | import org.forgerock.opendj.config.conditions.ContainsCondition; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.Argument; |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.CommandBuilder; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | 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.util.Pair; |
| | | |
| | | /** |
| | | * A sub-command handler which is used to modify the properties of a managed object. |
| | | * <p> |
| | | * This sub-command implements the various set-xxx-prop sub-commands. |
| | | */ |
| | | final class SetPropSubCommandHandler extends SubCommandHandler { |
| | | |
| | | /** |
| | | * Type of modification being performed. |
| | | */ |
| | | private static enum ModificationType { |
| | | /** |
| | | * Append a single value to the property. |
| | | */ |
| | | ADD, |
| | | |
| | | /** |
| | | * Remove a single value from the property. |
| | | */ |
| | | REMOVE, |
| | | |
| | | /** |
| | | * Append a single value to the property (first invocation removes existing values). |
| | | */ |
| | | SET; |
| | | } |
| | | |
| | | /** |
| | | * The value for the long option add. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_ADD = "add"; |
| | | |
| | | /** |
| | | * The value for the long option remove. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_REMOVE = "remove"; |
| | | |
| | | /** |
| | | * The value for the long option reset. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_RESET = "reset"; |
| | | |
| | | /** |
| | | * The value for the long option set. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_SET = "set"; |
| | | |
| | | /** |
| | | * The value for the short option add. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_ADD = null; |
| | | |
| | | /** |
| | | * The value for the short option remove. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_REMOVE = null; |
| | | |
| | | /** |
| | | * The value for the short option reset. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_RESET = null; |
| | | |
| | | /** |
| | | * The value for the short option set. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_SET = null; |
| | | |
| | | /** |
| | | * Creates a new set-xxx-prop sub-command for an instantiable relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The instantiable relation. |
| | | * @return Returns the new set-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static SetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | InstantiableRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new SetPropSubCommandHandler(parser, path.child(r, "DUMMY"), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new set-xxx-prop sub-command for an optional relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The optional relation. |
| | | * @return Returns the new set-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static SetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | OptionalRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new SetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new set-xxx-prop sub-command for a set relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The set relation. |
| | | * @return Returns the new set-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static SetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | SetRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new SetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new set-xxx-prop sub-command for a singleton relation. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @param path |
| | | * The parent managed object path. |
| | | * @param r |
| | | * The singleton relation. |
| | | * @return Returns the new set-xxx-prop sub-command. |
| | | * @throws ArgumentException |
| | | * If the sub-command could not be created successfully. |
| | | */ |
| | | public static SetPropSubCommandHandler create(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | SingletonRelationDefinition<?, ?> r) throws ArgumentException { |
| | | return new SetPropSubCommandHandler(parser, path.child(r), r); |
| | | } |
| | | |
| | | /** |
| | | * Configure the provided managed object and updates the command builder in the pased SetPropSubCommandHandler |
| | | * object. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param context |
| | | * The management context. |
| | | * @param mo |
| | | * The managed object to be configured. |
| | | * @param handler |
| | | * The SubCommandHandler whose command builder properties must be updated. |
| | | * @return Returns a MenuResult.success() if the managed object was configured successfully, or MenuResult.quit(), |
| | | * or MenuResult.cancel(), if the managed object was edited interactively and the user chose to quit or |
| | | * cancel. |
| | | * @throws ClientException |
| | | * If an unrecoverable client exception occurred whilst interacting with the server. |
| | | * @throws ClientException |
| | | * If an error occurred whilst interacting with the console. |
| | | */ |
| | | public static MenuResult<Void> modifyManagedObject(ConsoleApplication app, ManagementContext context, |
| | | ManagedObject<?> mo, SubCommandHandler handler) throws ClientException { |
| | | ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | |
| | | PropertyValueEditor editor = new PropertyValueEditor(app, context); |
| | | while (true) { |
| | | // Interactively set properties if applicable. |
| | | if (app.isInteractive()) { |
| | | SortedSet<PropertyDefinition<?>> properties = new TreeSet<PropertyDefinition<?>>(); |
| | | for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { |
| | | if (pd.hasOption(PropertyOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | properties.add(pd); |
| | | } |
| | | |
| | | MenuResult<Void> result = editor.edit(mo, properties, false); |
| | | |
| | | // Interactively enable/edit referenced components. |
| | | if (result.isSuccess()) { |
| | | result = checkReferences(app, context, mo, handler); |
| | | if (result.isAgain()) { |
| | | // Edit again. |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to cancel any changes. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_MODIFY_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | |
| | | try { |
| | | // Commit the changes if necessary |
| | | if (mo.isModified()) { |
| | | mo.commit(); |
| | | |
| | | // Output success message. |
| | | if (app.isVerbose() || app.isInteractive()) { |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_MODIFY_SUCCESS.get(ufn)); |
| | | } |
| | | |
| | | for (PropertyEditorModification<?> mod : editor.getModifications()) { |
| | | try { |
| | | handler.getCommandBuilder().addArgument(createArgument(mod)); |
| | | } catch (ArgumentException ae) { |
| | | // This is a bug |
| | | throw new RuntimeException("Unexpected error generating the command builder: " + ae, ae); |
| | | } |
| | | } |
| | | |
| | | handler.setCommandBuilderUseful(true); |
| | | } |
| | | |
| | | return MenuResult.success(); |
| | | } catch (MissingMandatoryPropertiesException e) { |
| | | if (app.isInteractive()) { |
| | | // If interactive, give the user the chance to fix the |
| | | // problems. |
| | | app.println(); |
| | | displayMissingMandatoryPropertyException(app, e); |
| | | app.println(); |
| | | if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } else { |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e); |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (OperationRejectedException e) { |
| | | if (app.isInteractive()) { |
| | | // If interactive, give the user the chance to fix the |
| | | // problems. |
| | | app.println(); |
| | | displayOperationRejectedException(app, e); |
| | | app.println(); |
| | | if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } else { |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e); |
| | | } |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectAlreadyExistsException e) { |
| | | // Should never happen. |
| | | throw new IllegalStateException(e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check that any referenced components are enabled if required. |
| | | */ |
| | | private static MenuResult<Void> checkReferences(ConsoleApplication app, ManagementContext context, |
| | | ManagedObject<?> mo, SubCommandHandler handler) throws ClientException { |
| | | ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); |
| | | LocalizableMessage ufn = d.getUserFriendlyName(); |
| | | |
| | | try { |
| | | for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { |
| | | if (pd instanceof AggregationPropertyDefinition<?, ?>) { |
| | | AggregationPropertyDefinition<?, ?> apd = (AggregationPropertyDefinition<?, ?>) pd; |
| | | |
| | | // Skip this aggregation if the referenced managed objects |
| | | // do not need to be enabled. |
| | | if (!apd.getTargetNeedsEnablingCondition().evaluate(context, mo)) { |
| | | continue; |
| | | } |
| | | |
| | | // The referenced component(s) must be enabled. |
| | | for (String name : mo.getPropertyValues(apd)) { |
| | | ManagedObjectPath<?, ?> path = apd.getChildPath(name); |
| | | LocalizableMessage rufn = path.getManagedObjectDefinition().getUserFriendlyName(); |
| | | ManagedObject<?> ref; |
| | | try { |
| | | ref = context.getManagedObject(path); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(rufn, rufn, rufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // FIXME: should not abort here. Instead, display the |
| | | // errors (if verbose) and apply the changes to the |
| | | // partial managed object. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(rufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(rufn); |
| | | throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg); |
| | | } |
| | | |
| | | Condition condition = apd.getTargetIsEnabledCondition(); |
| | | while (!condition.evaluate(context, ref)) { |
| | | boolean isBadReference = true; |
| | | |
| | | if (condition instanceof ContainsCondition) { |
| | | // Attempt to automatically enable the managed object. |
| | | ContainsCondition cvc = (ContainsCondition) condition; |
| | | app.println(); |
| | | if (app.confirmAction( |
| | | INFO_EDITOR_PROMPT_ENABLED_REFERENCED_COMPONENT.get(rufn, name, ufn), true)) { |
| | | cvc.setPropertyValue(ref); |
| | | try { |
| | | ref.commit(); |
| | | |
| | | // Try to create the command builder |
| | | if (app instanceof DSConfig && app.isInteractive()) { |
| | | DSConfig dsConfig = (DSConfig) app; |
| | | String subCommandName = "set-" + path.getRelationDefinition().getName() |
| | | + "-prop"; |
| | | CommandBuilder builder = dsConfig.getCommandBuilder(subCommandName); |
| | | |
| | | if (path.getRelationDefinition() |
| | | instanceof InstantiableRelationDefinition<?, ?>) { |
| | | String argName = CLIProfile.getInstance().getNamingArgument( |
| | | path.getRelationDefinition()); |
| | | try { |
| | | StringArgument arg = new StringArgument(argName, null, argName, |
| | | false, true, INFO_NAME_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_NAME.get(d.getUserFriendlyName())); |
| | | arg.addValue(name); |
| | | builder.addArgument(arg); |
| | | } catch (Throwable t) { |
| | | // Bug |
| | | throw new RuntimeException("Unexpected error: " + t, t); |
| | | } |
| | | } |
| | | |
| | | try { |
| | | StringArgument arg = new StringArgument(OPTION_DSCFG_LONG_SET, |
| | | OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, |
| | | true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | PropertyDefinition<?> propertyDefinition = cvc.getPropertyDefinition(); |
| | | arg.addValue(propertyDefinition.getName() + ':' |
| | | + castAndGetArgumentValue(propertyDefinition, cvc.getValue())); |
| | | builder.addArgument(arg); |
| | | } catch (Throwable t) { |
| | | // Bug |
| | | throw new RuntimeException("Unexpected error: " + t, t); |
| | | } |
| | | dsConfig.printCommandBuilder(builder); |
| | | } |
| | | |
| | | isBadReference = false; |
| | | } catch (MissingMandatoryPropertiesException e) { |
| | | // Give the user the chance to fix the problems. |
| | | app.println(); |
| | | displayMissingMandatoryPropertyException(app, e); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) { |
| | | MenuResult<Void> result = modifyManagedObject(app, context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (OperationRejectedException e) { |
| | | // Give the user the chance to fix the problems. |
| | | app.println(); |
| | | displayOperationRejectedException(app, e); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) { |
| | | MenuResult<Void> result = modifyManagedObject(app, context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } catch (ManagedObjectAlreadyExistsException e) { |
| | | // Should never happen. |
| | | throw new IllegalStateException(e); |
| | | } |
| | | } |
| | | } else { |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_TO_ENABLE.get(rufn, name, ufn), true)) { |
| | | MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app, |
| | | context, ref, handler); |
| | | if (result.isQuit()) { |
| | | return result; |
| | | } else if (result.isSuccess()) { |
| | | // The referenced component was modified |
| | | // successfully, but may still be disabled. |
| | | isBadReference = false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If the referenced component is still disabled because |
| | | // the user refused to modify it, then give the used the |
| | | // option of editing the referencing component. |
| | | if (isBadReference) { |
| | | app.println(); |
| | | app.println(ERR_SET_REFERENCED_COMPONENT_DISABLED.get(ufn, rufn)); |
| | | app.println(); |
| | | if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) { |
| | | return MenuResult.again(); |
| | | } else { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (ErrorResultException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_CE.get(ufn, e.getMessage()); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } |
| | | |
| | | return MenuResult.success(); |
| | | } |
| | | |
| | | /** The sub-commands naming arguments. */ |
| | | private final List<StringArgument> namingArgs; |
| | | |
| | | /** The path of the managed object. */ |
| | | private final ManagedObjectPath<?, ?> path; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property value adds. |
| | | */ |
| | | private final StringArgument propertyAddArgument; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property value removes. |
| | | */ |
| | | private final StringArgument propertyRemoveArgument; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property value resets. |
| | | */ |
| | | private final StringArgument propertyResetArgument; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property value assignments. |
| | | */ |
| | | private final StringArgument propertySetArgument; |
| | | |
| | | /** The sub-command associated with this handler. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** Private constructor. */ |
| | | private SetPropSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path, |
| | | RelationDefinition<?, ?> r) throws ArgumentException { |
| | | this.path = path; |
| | | |
| | | // Create the sub-command. |
| | | String name = "set-" + r.getName() + "-prop"; |
| | | LocalizableMessage description = INFO_DSCFG_DESCRIPTION_SUBCMD_SETPROP.get(r.getChildDefinition() |
| | | .getUserFriendlyName()); |
| | | this.subCommand = new SubCommand(parser, name, false, 0, 0, null, description); |
| | | |
| | | // Create the naming arguments. |
| | | this.namingArgs = createNamingArgs(subCommand, path, false); |
| | | |
| | | // Create the --set argument. |
| | | this.propertySetArgument = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, |
| | | OPTION_DSCFG_LONG_SET, false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | this.subCommand.addArgument(this.propertySetArgument); |
| | | |
| | | // Create the --reset argument. |
| | | this.propertyResetArgument = new StringArgument(OPTION_DSCFG_LONG_RESET, OPTION_DSCFG_SHORT_RESET, |
| | | OPTION_DSCFG_LONG_RESET, false, true, true, INFO_PROPERTY_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_RESET_PROP.get()); |
| | | this.subCommand.addArgument(this.propertyResetArgument); |
| | | |
| | | // Create the --add argument. |
| | | this.propertyAddArgument = new StringArgument(OPTION_DSCFG_LONG_ADD, OPTION_DSCFG_SHORT_ADD, |
| | | OPTION_DSCFG_LONG_ADD, false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_ADD_PROP_VAL.get()); |
| | | this.subCommand.addArgument(this.propertyAddArgument); |
| | | |
| | | // Create the --remove argument. |
| | | this.propertyRemoveArgument = new StringArgument(OPTION_DSCFG_LONG_REMOVE, OPTION_DSCFG_SHORT_REMOVE, |
| | | OPTION_DSCFG_LONG_REMOVE, false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get()); |
| | | this.subCommand.addArgument(this.propertyRemoveArgument); |
| | | |
| | | // Register the tags associated with the child managed objects. |
| | | addTags(path.getManagedObjectDefinition().getAllTags()); |
| | | } |
| | | |
| | | /** |
| | | * Gets the relation definition associated with the type of component that this sub-command handles. |
| | | * |
| | | * @return Returns the relation definition associated with the type of component that this sub-command handles. |
| | | */ |
| | | public RelationDefinition<?, ?> getRelationDefinition() { |
| | | return path.getRelationDefinition(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public SubCommand getSubCommand() { |
| | | return subCommand; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @SuppressWarnings({ "unchecked", "rawtypes" }) |
| | | @Override |
| | | public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException { |
| | | // Get the naming argument values. |
| | | List<String> names = getNamingArgValues(app, namingArgs); |
| | | |
| | | // Reset the command builder |
| | | getCommandBuilder().clearArguments(); |
| | | |
| | | setCommandBuilderUseful(false); |
| | | |
| | | // Update the command builder. |
| | | updateCommandBuilderWithSubCommand(); |
| | | |
| | | // Get the targeted managed object. |
| | | LocalizableMessage ufn = path.getRelationDefinition().getUserFriendlyName(); |
| | | ManagementContext context = factory.getManagementContext(app); |
| | | MenuResult<ManagedObject<?>> result; |
| | | try { |
| | | result = getManagedObject(app, context, path, names); |
| | | } catch (AuthorizationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(ufn); |
| | | throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg); |
| | | } catch (DefinitionDecodingException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(ufn, ufn, ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // FIXME: should not abort here. Instead, display the errors (if |
| | | // verbose) and apply the changes to the partial managed object. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(ufn); |
| | | throw new ClientException(ReturnCode.OTHER, msg, e); |
| | | } catch (ConcurrentModificationException e) { |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_MODIFY_CME.get(ufn); |
| | | throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | String objName = names.get(names.size() - 1); |
| | | ArgumentException except = null; |
| | | LocalizableMessage msg; |
| | | // if object name is 'null', get a user-friendly string to represent this |
| | | if (objName == null) { |
| | | msg = ERR_DSCFG_ERROR_FINDER_NO_CHILDREN_NULL.get(); |
| | | except = new ArgumentException(msg); |
| | | } else { |
| | | except = ArgumentExceptionFactory.unknownValueForChildComponent("\"" + objName + "\""); |
| | | } |
| | | if (app.isInteractive()) { |
| | | app.println(); |
| | | app.printVerboseMessage(except.getMessageObject()); |
| | | return MenuResult.cancel(); |
| | | } else { |
| | | throw except; |
| | | } |
| | | } |
| | | |
| | | if (result.isQuit()) { |
| | | if (!app.isMenuDrivenMode()) { |
| | | // User chose to quit. |
| | | app.println(); |
| | | app.println(INFO_DSCFG_CONFIRM_MODIFY_FAIL.get(ufn)); |
| | | } |
| | | return MenuResult.quit(); |
| | | } else if (result.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } |
| | | |
| | | ManagedObject<?> child = result.getValue(); |
| | | ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition(); |
| | | Map<String, ModificationType> lastModTypes = new HashMap<String, ModificationType>(); |
| | | Map<PropertyDefinition, Set> changes = new HashMap<PropertyDefinition, Set>(); |
| | | |
| | | // Reset properties. |
| | | for (String m : propertyResetArgument.getValues()) { |
| | | |
| | | // Check one does not try to reset with a value |
| | | if (m.contains(":")) { |
| | | throw ArgumentExceptionFactory.unableToResetPropertyWithValue(m, OPTION_DSCFG_LONG_RESET); |
| | | } |
| | | |
| | | PropertyDefinition<?> pd = getPropertyDefinition(d, m); |
| | | |
| | | // Mandatory properties which have no defined defaults cannot be reset. |
| | | if (pd.hasOption(PropertyOption.MANDATORY) |
| | | && pd.getDefaultBehaviorProvider() instanceof UndefinedDefaultBehaviorProvider) { |
| | | throw ArgumentExceptionFactory.unableToResetMandatoryProperty(d, m, OPTION_DSCFG_LONG_SET); |
| | | } |
| | | |
| | | // Save the modification type. |
| | | lastModTypes.put(m, ModificationType.SET); |
| | | |
| | | // Apply the modification. |
| | | modifyPropertyValues(child, pd, changes, ModificationType.SET, null); |
| | | } |
| | | |
| | | // Set properties. |
| | | for (String m : propertySetArgument.getValues()) { |
| | | Pair<String, String> pair = parseValue(m); |
| | | String propertyName = pair.getFirst(); |
| | | String value = pair.getSecond(); |
| | | |
| | | PropertyDefinition<?> pd = getPropertyDefinition(d, propertyName); |
| | | |
| | | // Apply the modification. |
| | | if (lastModTypes.containsKey(propertyName)) { |
| | | modifyPropertyValues(child, pd, changes, ModificationType.ADD, value); |
| | | } else { |
| | | lastModTypes.put(propertyName, ModificationType.SET); |
| | | modifyPropertyValues(child, pd, changes, ModificationType.SET, value); |
| | | } |
| | | } |
| | | |
| | | // Remove properties. |
| | | for (String m : propertyRemoveArgument.getValues()) { |
| | | Pair<String, String> pair = parseValue(m); |
| | | String propertyName = pair.getFirst(); |
| | | String value = pair.getSecond(); |
| | | |
| | | PropertyDefinition<?> pd = getPropertyDefinition(d, propertyName); |
| | | |
| | | // Apply the modification. |
| | | if (lastModTypes.containsKey(propertyName) && lastModTypes.get(propertyName) == ModificationType.SET) { |
| | | throw ArgumentExceptionFactory.incompatiblePropertyModification(m); |
| | | } |
| | | |
| | | lastModTypes.put(propertyName, ModificationType.REMOVE); |
| | | modifyPropertyValues(child, pd, changes, ModificationType.REMOVE, value); |
| | | } |
| | | |
| | | // Add properties. |
| | | for (String m : propertyAddArgument.getValues()) { |
| | | Pair<String, String> pair = parseValue(m); |
| | | String propertyName = pair.getFirst(); |
| | | String value = pair.getSecond(); |
| | | |
| | | PropertyDefinition<?> pd = getPropertyDefinition(d, propertyName); |
| | | |
| | | // Apply the modification. |
| | | if (lastModTypes.containsKey(propertyName) && lastModTypes.get(propertyName) == ModificationType.SET) { |
| | | throw ArgumentExceptionFactory.incompatiblePropertyModification(m); |
| | | } |
| | | |
| | | lastModTypes.put(propertyName, ModificationType.ADD); |
| | | modifyPropertyValues(child, pd, changes, ModificationType.ADD, value); |
| | | } |
| | | |
| | | // Apply the command line changes. |
| | | for (PropertyDefinition<?> pd : changes.keySet()) { |
| | | try { |
| | | child.setPropertyValues(pd, changes.get(pd)); |
| | | } catch (PropertyException e) { |
| | | throw ArgumentExceptionFactory.adaptPropertyException(e, d); |
| | | } |
| | | setCommandBuilderUseful(true); |
| | | } |
| | | |
| | | // Now the command line changes have been made, apply the changes |
| | | // interacting with the user to fix any problems if required. |
| | | MenuResult<Void> result2 = modifyManagedObject(app, context, child, this); |
| | | if (result2.isCancel()) { |
| | | return MenuResult.cancel(); |
| | | } else if (result2.isQuit()) { |
| | | return MenuResult.quit(); |
| | | } else { |
| | | if (propertyResetArgument.hasValue()) { |
| | | getCommandBuilder().addArgument(propertyResetArgument); |
| | | } |
| | | if (propertySetArgument.hasValue()) { |
| | | getCommandBuilder().addArgument(propertySetArgument); |
| | | } |
| | | if (propertyAddArgument.hasValue()) { |
| | | getCommandBuilder().addArgument(propertyAddArgument); |
| | | } |
| | | if (propertyRemoveArgument.hasValue()) { |
| | | getCommandBuilder().addArgument(propertyRemoveArgument); |
| | | } |
| | | return MenuResult.success(0); |
| | | } |
| | | } |
| | | |
| | | /** Parse and check the property "property:value". */ |
| | | private Pair<String, String> parseValue(String m) throws ArgumentException { |
| | | int sep = m.indexOf(':'); |
| | | if (sep < 0) { |
| | | throw ArgumentExceptionFactory.missingSeparatorInPropertyArgument(m); |
| | | } |
| | | if (sep == 0) { |
| | | throw ArgumentExceptionFactory.missingNameInPropertyArgument(m); |
| | | } |
| | | |
| | | String propertyName = m.substring(0, sep); |
| | | String value = m.substring(sep + 1, m.length()); |
| | | if (value.length() == 0) { |
| | | throw ArgumentExceptionFactory.missingValueInPropertyArgument(m); |
| | | } |
| | | return Pair.of(propertyName, value); |
| | | } |
| | | |
| | | /** Get and check the property definition. */ |
| | | private PropertyDefinition<?> getPropertyDefinition(ManagedObjectDefinition<?, ?> def, String propertyName) |
| | | throws ArgumentException { |
| | | try { |
| | | return def.getPropertyDefinition(propertyName); |
| | | } catch (IllegalArgumentException e) { |
| | | throw ArgumentExceptionFactory.unknownProperty(def, propertyName); |
| | | } |
| | | } |
| | | |
| | | /** Apply a single modification to the current change-set. */ |
| | | @SuppressWarnings({ "unchecked", "rawtypes" }) |
| | | private <T> void modifyPropertyValues(ManagedObject<?> mo, PropertyDefinition<T> pd, |
| | | Map<PropertyDefinition, Set> changes, ModificationType modType, String s) throws ArgumentException { |
| | | Set<T> values = changes.get(pd); |
| | | if (values == null) { |
| | | values = mo.getPropertyValues(pd); |
| | | } |
| | | |
| | | if (s == null || s.length() == 0) { |
| | | // Reset back to defaults. |
| | | values.clear(); |
| | | } else { |
| | | T value; |
| | | try { |
| | | value = pd.decodeValue(s); |
| | | } catch (PropertyException e) { |
| | | throw ArgumentExceptionFactory.adaptPropertyException(e, mo.getManagedObjectDefinition()); |
| | | } |
| | | |
| | | switch (modType) { |
| | | case ADD: |
| | | values.add(value); |
| | | break; |
| | | case REMOVE: |
| | | if (!values.remove(value)) { |
| | | // value was not part of values |
| | | throw ArgumentExceptionFactory.unknownValueForMultiValuedProperty(s, pd.getName()); |
| | | } |
| | | break; |
| | | case SET: |
| | | values = new TreeSet<T>(pd); |
| | | values.add(value); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | changes.put(pd, values); |
| | | } |
| | | |
| | | /** |
| | | * Creates an argument (the one that the user should provide in the command-line) that is equivalent to the |
| | | * modification proposed by the user in the provided PropertyEditorModification object. |
| | | * |
| | | * @param mod |
| | | * the object describing the modification made. |
| | | * @param <T> |
| | | * the type of the property to be retrieved. |
| | | * @return the argument representing the modification. |
| | | * @throws ArgumentException |
| | | * if there is a problem creating the argument. |
| | | */ |
| | | private static <T> Argument createArgument(PropertyEditorModification<T> mod) throws ArgumentException { |
| | | StringArgument arg; |
| | | |
| | | PropertyDefinition<T> propertyDefinition = mod.getPropertyDefinition(); |
| | | String propName = propertyDefinition.getName(); |
| | | |
| | | switch (mod.getType()) { |
| | | case RESET: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_RESET, OPTION_DSCFG_SHORT_RESET, OPTION_DSCFG_LONG_RESET, false, |
| | | true, true, INFO_PROPERTY_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_RESET_PROP.get()); |
| | | arg.addValue(propName); |
| | | break; |
| | | case REMOVE: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_REMOVE, OPTION_DSCFG_SHORT_REMOVE, OPTION_DSCFG_LONG_REMOVE, |
| | | false, true, true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | break; |
| | | case ADD: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_ADD, OPTION_DSCFG_SHORT_ADD, OPTION_DSCFG_LONG_ADD, false, true, |
| | | true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_ADD_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | break; |
| | | case SET: |
| | | arg = new StringArgument(OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, |
| | | true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_PROP_VAL.get()); |
| | | for (T value : mod.getModificationValues()) { |
| | | arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value)); |
| | | } |
| | | break; |
| | | default: |
| | | // Bug |
| | | throw new IllegalStateException("Unknown modification type: " + mod.getType()); |
| | | } |
| | | return arg; |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2011-2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static org.forgerock.opendj.config.dsconfig.DSConfig.PROPERTY_SCRIPT_NAME; |
| | | |
| | | import java.io.PrintStream; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.SortedMap; |
| | | import java.util.TreeMap; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.Configuration; |
| | | import org.forgerock.opendj.config.ConfigurationClient; |
| | | import org.forgerock.opendj.config.DefinitionDecodingException; |
| | | import org.forgerock.opendj.config.DurationUnit; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectNotFoundException; |
| | | import org.forgerock.opendj.config.ManagedObjectOption; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.ManagedObjectPathSerializer; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinition; |
| | | import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.SingletonRelationDefinition; |
| | | import org.forgerock.opendj.config.SizeUnit; |
| | | import org.forgerock.opendj.config.Tag; |
| | | import org.forgerock.opendj.config.client.ConcurrentModificationException; |
| | | import org.forgerock.opendj.config.client.IllegalManagedObjectNameException; |
| | | import org.forgerock.opendj.config.client.ManagedObject; |
| | | import org.forgerock.opendj.config.client.ManagedObjectDecodingException; |
| | | import org.forgerock.opendj.config.client.ManagementContext; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | |
| | | import com.forgerock.opendj.cli.Argument; |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.BooleanArgument; |
| | | import com.forgerock.opendj.cli.ClientException; |
| | | import com.forgerock.opendj.cli.CommandBuilder; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.Menu; |
| | | import com.forgerock.opendj.cli.MenuBuilder; |
| | | 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.TabSeparatedTablePrinter; |
| | | import com.forgerock.opendj.cli.TablePrinter; |
| | | |
| | | /** |
| | | * An interface for sub-command implementations. |
| | | */ |
| | | abstract class SubCommandHandler implements Comparable<SubCommandHandler> { |
| | | |
| | | /** |
| | | * A path serializer which is used to retrieve a managed object based on a path and a list of path arguments. |
| | | */ |
| | | private class ManagedObjectFinder implements ManagedObjectPathSerializer { |
| | | |
| | | /** The console application. */ |
| | | private ConsoleApplication app; |
| | | |
| | | /** The index of the next path argument to be retrieved. */ |
| | | private int argIndex; |
| | | |
| | | /** The list of managed object path arguments. */ |
| | | private List<String> args; |
| | | |
| | | private AuthorizationException authze; |
| | | |
| | | private ErrorResultException ere; |
| | | |
| | | /** |
| | | * Any CLI exception that was caught when attempting to find the managed object. |
| | | */ |
| | | private ClientException clie; |
| | | |
| | | private ConcurrentModificationException cme; |
| | | |
| | | /** |
| | | * Any operation exception that was caught when attempting to find the managed object. |
| | | */ |
| | | private DefinitionDecodingException dde; |
| | | |
| | | private ManagedObjectDecodingException mode; |
| | | |
| | | private ManagedObjectNotFoundException monfe; |
| | | |
| | | /** The current result. */ |
| | | private MenuResult<ManagedObject<?>> result; |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d, |
| | | String name) { |
| | | if (result.isSuccess()) { |
| | | // We should ignore the "template" name here and use a path |
| | | // argument. |
| | | String childName = args.get(argIndex++); |
| | | |
| | | try { |
| | | // If the name is null then we must be interactive - so let |
| | | // the user choose. |
| | | if (childName == null) { |
| | | try { |
| | | MenuResult<String> sresult = readChildName(app, result.getValue(), r, d); |
| | | |
| | | if (sresult.isCancel()) { |
| | | result = MenuResult.cancel(); |
| | | return; |
| | | } else if (sresult.isQuit()) { |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } else { |
| | | childName = sresult.getValue(); |
| | | } |
| | | } catch (ClientException e) { |
| | | clie = e; |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } |
| | | } else if (childName.trim().length() == 0) { |
| | | IllegalManagedObjectNameException e = new IllegalManagedObjectNameException(childName); |
| | | clie = ArgumentExceptionFactory.adaptIllegalManagedObjectNameException(e, d); |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } |
| | | |
| | | ManagedObject<?> child = result.getValue().getChild(r, childName); |
| | | |
| | | // Check that child is a sub-type of the specified |
| | | // definition. |
| | | if (!child.getManagedObjectDefinition().isChildOf(d)) { |
| | | clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child.getManagedObjectDefinition(), |
| | | getSubCommand().getName()); |
| | | result = MenuResult.quit(); |
| | | } else { |
| | | result = MenuResult.<ManagedObject<?>> success(child); |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | dde = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | mode = e; |
| | | result = MenuResult.quit(); |
| | | } catch (AuthorizationException e) { |
| | | authze = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | monfe = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ConcurrentModificationException e) { |
| | | cme = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ErrorResultException e) { |
| | | ere = e; |
| | | result = MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | if (result.isSuccess()) { |
| | | try { |
| | | ManagedObject<?> child = result.getValue().getChild(r); |
| | | |
| | | // Check that child is a sub-type of the specified |
| | | // definition. |
| | | if (!child.getManagedObjectDefinition().isChildOf(d)) { |
| | | clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child.getManagedObjectDefinition(), |
| | | getSubCommand().getName()); |
| | | result = MenuResult.quit(); |
| | | } else { |
| | | result = MenuResult.<ManagedObject<?>> success(child); |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | dde = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | mode = e; |
| | | result = MenuResult.quit(); |
| | | } catch (AuthorizationException e) { |
| | | authze = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | monfe = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ConcurrentModificationException e) { |
| | | cme = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ErrorResultException e) { |
| | | ere = e; |
| | | result = MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | if (result.isSuccess()) { |
| | | // We should ignore the "template" name here and use a path |
| | | // argument. |
| | | String childName = args.get(argIndex++); |
| | | |
| | | try { |
| | | // If the name is null then we must be interactive - so let |
| | | // the user choose. |
| | | if (childName == null) { |
| | | try { |
| | | MenuResult<String> sresult = readChildName(app, result.getValue(), r, d); |
| | | |
| | | if (sresult.isCancel()) { |
| | | result = MenuResult.cancel(); |
| | | return; |
| | | } else if (sresult.isQuit()) { |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } else { |
| | | childName = sresult.getValue(); |
| | | } |
| | | } catch (ClientException e) { |
| | | clie = e; |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } |
| | | } else if (childName.trim().length() == 0) { |
| | | IllegalManagedObjectNameException e = new IllegalManagedObjectNameException(childName); |
| | | clie = ArgumentExceptionFactory.adaptIllegalManagedObjectNameException(e, d); |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } else { |
| | | String name = childName.trim(); |
| | | SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> types = getSubTypes(d); |
| | | ManagedObjectDefinition<?, ?> cd = types.get(name); |
| | | if (cd == null) { |
| | | // The name must be invalid. |
| | | String typeUsage = getSubTypesUsage(d); |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED.get(name, |
| | | r.getUserFriendlyName(), typeUsage); |
| | | clie = new ClientException(ReturnCode.APPLICATION_ERROR, msg); |
| | | result = MenuResult.quit(); |
| | | return; |
| | | } else { |
| | | childName = cd.getName(); |
| | | } |
| | | } |
| | | |
| | | ManagedObject<?> child = result.getValue().getChild(r, childName); |
| | | |
| | | // Check that child is a sub-type of the specified |
| | | // definition. |
| | | if (!child.getManagedObjectDefinition().isChildOf(d)) { |
| | | clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child.getManagedObjectDefinition(), |
| | | getSubCommand().getName()); |
| | | result = MenuResult.quit(); |
| | | } else { |
| | | result = MenuResult.<ManagedObject<?>> success(child); |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | dde = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | mode = e; |
| | | result = MenuResult.quit(); |
| | | } catch (AuthorizationException e) { |
| | | authze = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | monfe = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ConcurrentModificationException e) { |
| | | cme = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ErrorResultException e) { |
| | | ere = e; |
| | | result = MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | if (result.isSuccess()) { |
| | | try { |
| | | ManagedObject<?> child = result.getValue().getChild(r); |
| | | |
| | | // Check that child is a sub-type of the specified |
| | | // definition. |
| | | if (!child.getManagedObjectDefinition().isChildOf(d)) { |
| | | clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child.getManagedObjectDefinition(), |
| | | getSubCommand().getName()); |
| | | result = MenuResult.quit(); |
| | | } else { |
| | | result = MenuResult.<ManagedObject<?>> success(child); |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | dde = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | mode = e; |
| | | result = MenuResult.quit(); |
| | | } catch (AuthorizationException e) { |
| | | authze = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | monfe = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ConcurrentModificationException e) { |
| | | cme = e; |
| | | result = MenuResult.quit(); |
| | | } catch (ErrorResultException e) { |
| | | ere = e; |
| | | result = MenuResult.quit(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Finds the named managed object. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param context |
| | | * The management context. |
| | | * @param path |
| | | * The managed object path. |
| | | * @param args |
| | | * The managed object path arguments. |
| | | * @return Returns a {@link MenuResult#success()} containing the managed object referenced by the provided |
| | | * managed object path, or {@link MenuResult#quit()}, or {@link MenuResult#cancel()}, if the sub-command |
| | | * was run interactively and the user chose to quit or cancel. |
| | | * @throws ClientException |
| | | * If one of the naming arguments referenced a managed object of the wrong type. |
| | | * @throws DefinitionDecodingException |
| | | * If the managed object was found but its type could not be determined. |
| | | * @throws ManagedObjectDecodingException |
| | | * If the managed object was found but one or more of its properties could not be decoded. |
| | | * @throws ManagedObjectNotFoundException |
| | | * If the requested managed object could not be found on the server. |
| | | * @throws ConcurrentModificationException |
| | | * If this managed object has been removed from the server by another client. |
| | | * @throws AuthorizationException |
| | | * If the server refuses to retrieve the managed object because the client does not have the correct |
| | | * privileges. |
| | | */ |
| | | public MenuResult<ManagedObject<?>> find(ConsoleApplication app, ManagementContext context, |
| | | ManagedObjectPath<?, ?> path, List<String> args) throws ClientException, AuthorizationException, |
| | | ConcurrentModificationException, DefinitionDecodingException, ManagedObjectDecodingException, |
| | | ManagedObjectNotFoundException { |
| | | this.result = MenuResult.<ManagedObject<?>> success(context.getRootConfigurationManagedObject()); |
| | | this.app = app; |
| | | this.args = args; |
| | | this.argIndex = 0; |
| | | |
| | | this.clie = null; |
| | | this.authze = null; |
| | | this.cme = null; |
| | | this.dde = null; |
| | | this.mode = null; |
| | | this.monfe = null; |
| | | |
| | | path.serialize(this); |
| | | |
| | | if (result.isSuccess()) { |
| | | return result; |
| | | } else if (clie != null) { |
| | | throw clie; |
| | | } else if (authze != null) { |
| | | throw authze; |
| | | } else if (cme != null) { |
| | | throw cme; |
| | | } else if (dde != null) { |
| | | throw dde; |
| | | } else if (mode != null) { |
| | | throw mode; |
| | | } else if (monfe != null) { |
| | | throw monfe; |
| | | } else { |
| | | // User requested termination interactively. |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A path serializer which is used to register a sub-command's naming arguments. |
| | | */ |
| | | protected static final class NamingArgumentBuilder implements ManagedObjectPathSerializer { |
| | | |
| | | /** |
| | | * Creates the naming arguments for a given path. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @param path |
| | | * The managed object path. |
| | | * @param isCreate |
| | | * Indicates whether the sub-command is a create-xxx sub-command, in which case the final path |
| | | * element will have different usage information. |
| | | * @return Returns the naming arguments. |
| | | * @throws ArgumentException |
| | | * If one or more naming arguments could not be registered. |
| | | */ |
| | | public static List<StringArgument> create(SubCommand subCommand, ManagedObjectPath<?, ?> path, boolean isCreate) |
| | | throws ArgumentException { |
| | | NamingArgumentBuilder builder = new NamingArgumentBuilder(subCommand, path.size(), isCreate); |
| | | path.serialize(builder); |
| | | |
| | | if (builder.e != null) { |
| | | throw builder.e; |
| | | } |
| | | |
| | | return builder.arguments; |
| | | } |
| | | |
| | | /** The list of naming arguments. */ |
| | | private final List<StringArgument> arguments = new LinkedList<StringArgument>(); |
| | | |
| | | /** |
| | | * Any argument exception thrown when creating the naming arguments. |
| | | */ |
| | | private ArgumentException e = null; |
| | | |
| | | /** |
| | | * Indicates whether the sub-command is a create-xxx sub-command, in which case the final path element will have |
| | | * different usage information. |
| | | */ |
| | | private final boolean isCreate; |
| | | |
| | | /** The sub-command. */ |
| | | private final SubCommand subCommand; |
| | | |
| | | /** The number of path elements to expect. */ |
| | | private int sz; |
| | | |
| | | /** Private constructor. */ |
| | | private NamingArgumentBuilder(SubCommand subCommand, int sz, boolean isCreate) { |
| | | this.subCommand = subCommand; |
| | | this.sz = sz; |
| | | this.isCreate = isCreate; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d, |
| | | String name) { |
| | | sz--; |
| | | |
| | | String argName = CLIProfile.getInstance().getNamingArgument(r); |
| | | StringArgument arg; |
| | | |
| | | try { |
| | | if (isCreate && sz == 0) { |
| | | // The final path element in create-xxx sub-commands should |
| | | // have a different usage. |
| | | PropertyDefinition<?> pd = r.getNamingPropertyDefinition(); |
| | | |
| | | if (pd != null) { |
| | | // Use syntax and description from naming property. |
| | | PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(false); |
| | | LocalizableMessage usage = LocalizableMessage.raw("{" + b.getUsage(pd) + "}"); |
| | | arg = new StringArgument(argName, null, argName, false, true, usage, |
| | | INFO_DSCFG_DESCRIPTION_NAME_CREATE_EXT.get(d.getUserFriendlyName(), pd.getName(), |
| | | pd.getSynopsis())); |
| | | } else { |
| | | arg = new StringArgument(argName, null, argName, false, true, INFO_NAME_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName())); |
| | | } |
| | | } else { |
| | | // A normal naming argument. |
| | | arg = new StringArgument(argName, null, argName, false, true, INFO_NAME_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_NAME.get(d.getUserFriendlyName())); |
| | | } |
| | | subCommand.addArgument(arg); |
| | | arguments.add(arg); |
| | | } catch (ArgumentException e) { |
| | | this.e = e; |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | sz--; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | sz--; |
| | | |
| | | // The name of the managed object is determined from its type, so |
| | | // we don't need this argument. |
| | | if (isCreate && sz == 0) { |
| | | return; |
| | | } |
| | | |
| | | String argName = CLIProfile.getInstance().getNamingArgument(r); |
| | | StringArgument arg; |
| | | |
| | | try { |
| | | arg = new StringArgument(argName, null, argName, false, true, INFO_NAME_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_NAME.get(d.getUserFriendlyName())); |
| | | subCommand.addArgument(arg); |
| | | arguments.add(arg); |
| | | } catch (ArgumentException e) { |
| | | this.e = e; |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement( |
| | | SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) { |
| | | sz--; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * The threshold above which choice menus should be displayed in multiple columns. |
| | | */ |
| | | public static final int MULTI_COLUMN_THRESHOLD = 8; |
| | | |
| | | /** |
| | | * The value for the long option property. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_PROPERTY = "property"; |
| | | |
| | | /** |
| | | * The value for the long option record. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_RECORD = "record"; |
| | | |
| | | /** |
| | | * The value for the long option unit-size. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_UNIT_SIZE = "unit-size"; |
| | | |
| | | /** |
| | | * The value for the long option unit-time. |
| | | */ |
| | | private static final String OPTION_DSCFG_LONG_UNIT_TIME = "unit-time"; |
| | | |
| | | /** |
| | | * The value for the short option property. |
| | | */ |
| | | private static final Character OPTION_DSCFG_SHORT_PROPERTY = null; |
| | | |
| | | /** |
| | | * The value for the short option record. |
| | | */ |
| | | private static final char OPTION_DSCFG_SHORT_RECORD = 'E'; |
| | | |
| | | /** |
| | | * The value for the short option unit-size. |
| | | */ |
| | | private static final char OPTION_DSCFG_SHORT_UNIT_SIZE = 'z'; |
| | | |
| | | /** |
| | | * The value for the short option unit-time. |
| | | */ |
| | | private static final char OPTION_DSCFG_SHORT_UNIT_TIME = 'm'; |
| | | |
| | | /** |
| | | * The argument which should be used to specify zero or more property names. |
| | | */ |
| | | private StringArgument propertyArgument; |
| | | |
| | | /** The argument which should be used to request record mode. */ |
| | | private BooleanArgument recordModeArgument; |
| | | |
| | | /** The tags associated with this sub-command handler. */ |
| | | private final Set<Tag> tags = new HashSet<Tag>(); |
| | | |
| | | /** The argument which should be used to request specific size units. */ |
| | | private StringArgument unitSizeArgument; |
| | | |
| | | /** The argument which should be used to request specific time units. */ |
| | | private StringArgument unitTimeArgument; |
| | | |
| | | /** The command builder associated with this handler. */ |
| | | private CommandBuilder commandBuilder; |
| | | |
| | | /** |
| | | * The boolean that says whether is useful to display the command builder's contents after calling the run method or |
| | | * not. |
| | | */ |
| | | private boolean isCommandBuilderUseful = true; |
| | | |
| | | /** |
| | | * Create a new sub-command handler. |
| | | */ |
| | | protected SubCommandHandler() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public final int compareTo(SubCommandHandler o) { |
| | | String s1 = getSubCommand().getName(); |
| | | String s2 = o.getSubCommand().getName(); |
| | | |
| | | return s1.compareTo(s2); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public final boolean equals(Object obj) { |
| | | if (this == obj) { |
| | | return true; |
| | | } else if (obj instanceof SubCommandHandler) { |
| | | SubCommandHandler other = (SubCommandHandler) obj; |
| | | |
| | | String s1 = getSubCommand().getName(); |
| | | String s2 = other.getSubCommand().getName(); |
| | | return s1.equals(s2); |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Gets the sub-command associated with this handler. |
| | | * |
| | | * @return Returns the sub-command associated with this handler. |
| | | */ |
| | | public abstract SubCommand getSubCommand(); |
| | | |
| | | /** |
| | | * Gets the command builder associated with this handler. The method should be called after calling |
| | | * <CODE>run()</CODE> method. |
| | | * |
| | | * @return Returns the sub-command associated with this handler. |
| | | */ |
| | | public final CommandBuilder getCommandBuilder() { |
| | | if (commandBuilder == null) { |
| | | commandBuilder = new CommandBuilder(System.getProperty(PROPERTY_SCRIPT_NAME), getSubCommand().getName()); |
| | | } |
| | | return commandBuilder; |
| | | } |
| | | |
| | | /** |
| | | * This method tells whether displaying the command builder contents makes sense or not. For instance in the case of |
| | | * the help subcommand handler displaying information makes no much sense. |
| | | * |
| | | * @return <CODE>true</CODE> if displaying the command builder is useful and <CODE>false</CODE> otherwise. |
| | | */ |
| | | public final boolean isCommandBuilderUseful() { |
| | | return isCommandBuilderUseful; |
| | | } |
| | | |
| | | /** |
| | | * Sets wheter the command builder is useful or not. |
| | | * |
| | | * @param isCommandBuilderUseful |
| | | * whether the command builder is useful or not. |
| | | */ |
| | | protected final void setCommandBuilderUseful(boolean isCommandBuilderUseful) { |
| | | this.isCommandBuilderUseful = isCommandBuilderUseful; |
| | | } |
| | | |
| | | /** |
| | | * Gets the tags associated with this sub-command handler. |
| | | * |
| | | * @return Returns the tags associated with this sub-command handler. |
| | | */ |
| | | public final Set<Tag> getTags() { |
| | | return tags; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public final int hashCode() { |
| | | return getSubCommand().getName().hashCode(); |
| | | } |
| | | |
| | | /** |
| | | * Run this sub-command handler. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param factory |
| | | * The LDAP management context factory context factory. |
| | | * @return Returns a {@link MenuResult#success()} containing zero if the sub-command completed successfully or |
| | | * non-zero if it did not, or {@link MenuResult#quit()}, or {@link MenuResult#cancel()}, if the sub-command |
| | | * was run interactively and the user chose to quit or cancel. |
| | | * @throws ArgumentException |
| | | * If an argument required by the sub-command could not be parsed successfully. |
| | | * @throws ClientException |
| | | * If the management context could not be created. |
| | | */ |
| | | public abstract MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) |
| | | throws ArgumentException, ClientException; |
| | | |
| | | /** |
| | | * Get the string representation of this sub-command handler. |
| | | * <p> |
| | | * The string representation is simply the sub-command's name. |
| | | * |
| | | * @return Returns the string representation of this sub-command handler. |
| | | */ |
| | | @Override |
| | | public final String toString() { |
| | | return getSubCommand().getName(); |
| | | } |
| | | |
| | | /** |
| | | * Adds one or more tags to this sub-command handler. |
| | | * |
| | | * @param tags |
| | | * The tags to be added to this sub-command handler. |
| | | */ |
| | | protected final void addTags(Collection<Tag> tags) { |
| | | this.tags.addAll(tags); |
| | | } |
| | | |
| | | /** |
| | | * Adds one or more tags to this sub-command handler. |
| | | * |
| | | * @param tags |
| | | * The tags to be added to this sub-command handler. |
| | | */ |
| | | protected final void addTags(Tag... tags) { |
| | | addTags(Arrays.asList(tags)); |
| | | } |
| | | |
| | | /** |
| | | * Creates the naming arguments for a given path and registers them. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @param p |
| | | * The managed object path. |
| | | * @param isCreate |
| | | * Indicates whether the sub-command is a create-xxx sub-command, in which case the final path element |
| | | * will have different usage information. |
| | | * @return Returns the naming arguments. |
| | | * @throws ArgumentException |
| | | * If one or more naming arguments could not be registered. |
| | | */ |
| | | protected final List<StringArgument> createNamingArgs(SubCommand subCommand, ManagedObjectPath<?, ?> p, |
| | | boolean isCreate) throws ArgumentException { |
| | | return NamingArgumentBuilder.create(subCommand, p, isCreate); |
| | | } |
| | | |
| | | /** |
| | | * Creates a script-friendly table printer. This factory method should be used by sub-command handler |
| | | * implementations rather than constructing a table printer directly so that we can easily switch table |
| | | * implementations (perhaps dynamically depending on argument). |
| | | * |
| | | * @param stream |
| | | * The output stream for the table. |
| | | * @return Returns a script-friendly table printer. |
| | | */ |
| | | protected final TablePrinter createScriptFriendlyTablePrinter(PrintStream stream) { |
| | | return new TabSeparatedTablePrinter(stream); |
| | | } |
| | | |
| | | /** |
| | | * Get the managed object referenced by the provided managed object path. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param context |
| | | * The management context. |
| | | * @param path |
| | | * The managed object path. |
| | | * @param args |
| | | * The list of managed object names required by the path. |
| | | * @return Returns a {@link MenuResult#success()} containing the managed object referenced by the provided managed |
| | | * object path, or {@link MenuResult#quit()}, or {@link MenuResult#cancel()}, if the sub-command was run |
| | | * interactively and the user chose to quit or cancel. |
| | | * @throws DefinitionDecodingException |
| | | * If the managed object was found but its type could not be determined. |
| | | * @throws ManagedObjectDecodingException |
| | | * If the managed object was found but one or more of its properties could not be decoded. |
| | | * @throws ManagedObjectNotFoundException |
| | | * If the requested managed object could not be found on the server. |
| | | * @throws ConcurrentModificationException |
| | | * If this managed object has been removed from the server by another client. |
| | | * @throws AuthorizationException |
| | | * If the server refuses to retrieve the managed object because the client does not have the correct |
| | | * privileges. |
| | | * @throws ClientException |
| | | * If one of the naming arguments referenced a managed object of the wrong type. |
| | | * @throws ClientException |
| | | * If the management context could not be created. |
| | | */ |
| | | protected final MenuResult<ManagedObject<?>> getManagedObject(ConsoleApplication app, ManagementContext context, |
| | | ManagedObjectPath<?, ?> path, List<String> args) throws ClientException, AuthorizationException, |
| | | DefinitionDecodingException, ManagedObjectDecodingException, ConcurrentModificationException, |
| | | ManagedObjectNotFoundException { |
| | | ManagedObjectFinder finder = new ManagedObjectFinder(); |
| | | return finder.find(app, context, path, args); |
| | | } |
| | | |
| | | /** |
| | | * Gets the values of the naming arguments. |
| | | * |
| | | * @param app |
| | | * The console application. |
| | | * @param namingArgs |
| | | * The naming arguments. |
| | | * @return Returns the values of the naming arguments. |
| | | * @throws ArgumentException |
| | | * If one of the naming arguments is missing and the application is non-interactive. |
| | | */ |
| | | protected final List<String> getNamingArgValues(ConsoleApplication app, List<StringArgument> namingArgs) |
| | | throws ArgumentException { |
| | | ArrayList<String> values = new ArrayList<String>(namingArgs.size()); |
| | | for (StringArgument arg : namingArgs) { |
| | | String value = arg.getValue(); |
| | | |
| | | if (value == null && !app.isInteractive()) { |
| | | throw ArgumentExceptionFactory.missingMandatoryNonInteractiveArgument(arg); |
| | | } else { |
| | | values.add(value); |
| | | } |
| | | } |
| | | return values; |
| | | } |
| | | |
| | | /** |
| | | * Gets the optional list of property names that the user requested. |
| | | * |
| | | * @return Returns the optional list of property names that the user requested. |
| | | */ |
| | | protected final Set<String> getPropertyNames() { |
| | | if (propertyArgument != null) { |
| | | return new LinkedHashSet<String>(propertyArgument.getValues()); |
| | | } else { |
| | | return Collections.emptySet(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Gets the optional size unit that the user requested. |
| | | * |
| | | * @return Returns the size unit that the user requested, or <code>null</code> if no size unit was specified. |
| | | * @throws ArgumentException |
| | | * If the user specified an invalid size unit. |
| | | */ |
| | | protected final SizeUnit getSizeUnit() throws ArgumentException { |
| | | if (unitSizeArgument != null) { |
| | | String value = unitSizeArgument.getValue(); |
| | | |
| | | if (value != null) { |
| | | try { |
| | | return SizeUnit.getUnit(value); |
| | | } catch (IllegalArgumentException e) { |
| | | LocalizableMessage msg = INFO_DSCFG_ERROR_SIZE_UNIT_UNRECOGNIZED.get(value); |
| | | throw new ArgumentException(msg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Gets the optional time unit that the user requested. |
| | | * |
| | | * @return Returns the time unit that the user requested, or <code>null</code> if no time unit was specified. |
| | | * @throws ArgumentException |
| | | * If the user specified an invalid time unit. |
| | | */ |
| | | protected final DurationUnit getTimeUnit() throws ArgumentException { |
| | | if (unitTimeArgument != null) { |
| | | String value = unitTimeArgument.getValue(); |
| | | |
| | | if (value != null) { |
| | | try { |
| | | return DurationUnit.getUnit(value); |
| | | } catch (IllegalArgumentException e) { |
| | | LocalizableMessage msg = INFO_DSCFG_ERROR_TIME_UNIT_UNRECOGNIZED.get(value); |
| | | throw new ArgumentException(msg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Determines whether the user requested record-mode. |
| | | * |
| | | * @return Returns <code>true</code> if the user requested record-mode. |
| | | */ |
| | | protected final boolean isRecordMode() { |
| | | if (recordModeArgument != null) { |
| | | return recordModeArgument.isPresent(); |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Interactively prompts the user to select from a choice of child managed objects. |
| | | * <p> |
| | | * This method will adapt according to the available choice. For example, if there is only one choice, then a |
| | | * question will be asked. If there are no children then an <code>ArgumentException</code> will be thrown. |
| | | * |
| | | * @param <C> |
| | | * The type of child client configuration. |
| | | * @param <S> |
| | | * The type of child server configuration. |
| | | * @param app |
| | | * The console application. |
| | | * @param parent |
| | | * The parent managed object. |
| | | * @param r |
| | | * The relation between the parent and the children, must be a set or instantiable relation. |
| | | * @param d |
| | | * The type of child managed object to choose from. |
| | | * @return Returns a {@link MenuResult#success()} containing the name of the managed object that the user selected, |
| | | * or {@link MenuResult#quit()}, or {@link MenuResult#cancel()}, if the sub-command was run interactive and |
| | | * the user chose to quit or cancel. |
| | | * @throws ConcurrentModificationException |
| | | * If the parent managed object has been deleted. |
| | | * @throws AuthorizationException |
| | | * If the children cannot be listed due to an authorization failure. |
| | | * @throws ClientException |
| | | * If the user input can be read from the console or if there are no children. |
| | | */ |
| | | protected final <C extends ConfigurationClient, S extends Configuration> MenuResult<String> readChildName( |
| | | ConsoleApplication app, ManagedObject<?> parent, RelationDefinition<C, S> r, |
| | | AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws AuthorizationException, |
| | | ConcurrentModificationException, ClientException { |
| | | if (d == null) { |
| | | d = r.getChildDefinition(); |
| | | } |
| | | |
| | | app.println(); |
| | | app.println(); |
| | | |
| | | // Filter out advanced and hidden types if required. |
| | | String[] childNames = null; |
| | | try { |
| | | if (r instanceof InstantiableRelationDefinition) { |
| | | childNames = parent.listChildren((InstantiableRelationDefinition<C, S>) r, d); |
| | | } else { |
| | | childNames = parent.listChildren((SetRelationDefinition<C, S>) r, d); |
| | | } |
| | | } catch (ErrorResultException e) { |
| | | // FIXME check exceptions |
| | | System.out.println(String.format("An error occured %s", e.getMessage())); |
| | | } |
| | | |
| | | SortedMap<String, String> children = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); |
| | | |
| | | for (String childName : childNames) { |
| | | ManagedObject<?> child; |
| | | try { |
| | | if (r instanceof InstantiableRelationDefinition) { |
| | | child = parent.getChild((InstantiableRelationDefinition<C, S>) r, childName); |
| | | } else { |
| | | child = parent.getChild((SetRelationDefinition<C, S>) r, childName); |
| | | } |
| | | |
| | | ManagedObjectDefinition<?, ?> cd = child.getManagedObjectDefinition(); |
| | | |
| | | if (cd.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!app.isAdvancedMode() && cd.hasOption(ManagedObjectOption.ADVANCED)) { |
| | | continue; |
| | | } |
| | | |
| | | if (r instanceof InstantiableRelationDefinition) { |
| | | children.put(childName, childName); |
| | | } else { |
| | | // For sets the RDN is the type string, the ufn is more friendly. |
| | | children.put(cd.getUserFriendlyName().toString(), childName); |
| | | } |
| | | } catch (DefinitionDecodingException e) { |
| | | // Add it anyway: maybe the user is trying to fix the problem. |
| | | children.put(childName, childName); |
| | | } catch (ManagedObjectDecodingException e) { |
| | | // Add it anyway: maybe the user is trying to fix the problem. |
| | | children.put(childName, childName); |
| | | } catch (ManagedObjectNotFoundException e) { |
| | | // Skip it - the managed object has been concurrently removed. |
| | | } catch (ErrorResultException e) { |
| | | // Add it anyway: maybe the user is trying to fix the problem. |
| | | children.put(childName, childName); |
| | | } |
| | | } |
| | | |
| | | switch (children.size()) { |
| | | case 0: { |
| | | // No options available - abort. |
| | | LocalizableMessage msg = ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(d.getUserFriendlyPluralName()); |
| | | app.println(msg); |
| | | return MenuResult.cancel(); |
| | | } |
| | | case 1: { |
| | | // Only one option available so confirm that the user wishes to |
| | | // access it. |
| | | LocalizableMessage msg = INFO_DSCFG_FINDER_PROMPT_SINGLE.get(d.getUserFriendlyName(), children.firstKey()); |
| | | if (app.confirmAction(msg, true)) { |
| | | try { |
| | | String argName = CLIProfile.getInstance().getNamingArgument(r); |
| | | StringArgument arg = new StringArgument(argName, null, argName, false, true, |
| | | INFO_NAME_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName())); |
| | | if (r instanceof InstantiableRelationDefinition) { |
| | | arg.addValue(children.get(children.firstKey())); |
| | | } else { |
| | | // Set relation: need the short type name. |
| | | String friendlyName = children.firstKey(); |
| | | String shortName = children.get(friendlyName); |
| | | try { |
| | | AbstractManagedObjectDefinition<?, ?> cd = null; |
| | | try { |
| | | cd = d.getChild(shortName); |
| | | } catch (IllegalArgumentException e) { |
| | | // Last resource: try with friendly name |
| | | cd = d.getChild(friendlyName); |
| | | } |
| | | arg.addValue(getShortTypeName(r.getChildDefinition(), cd)); |
| | | } catch (IllegalArgumentException e) { |
| | | arg.addValue(shortName); |
| | | } |
| | | } |
| | | getCommandBuilder().addArgument(arg); |
| | | } catch (Throwable t) { |
| | | // Bug |
| | | throw new RuntimeException("Unexpected exception: " + t, t); |
| | | } |
| | | return MenuResult.success(children.get(children.firstKey())); |
| | | } else { |
| | | return MenuResult.cancel(); |
| | | } |
| | | } |
| | | default: { |
| | | // Display a menu. |
| | | MenuBuilder<String> builder = new MenuBuilder<String>(app); |
| | | builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD); |
| | | builder.setPrompt(INFO_DSCFG_FINDER_PROMPT_MANY.get(d.getUserFriendlyName())); |
| | | |
| | | for (Map.Entry<String, String> child : children.entrySet()) { |
| | | LocalizableMessage option = LocalizableMessage.raw("%s", child.getKey()); |
| | | builder.addNumberedOption(option, MenuResult.success(child.getValue())); |
| | | } |
| | | |
| | | if (app.isMenuDrivenMode()) { |
| | | builder.addCancelOption(true); |
| | | } |
| | | builder.addQuitOption(); |
| | | |
| | | Menu<String> menu = builder.toMenu(); |
| | | MenuResult<String> result = menu.run(); |
| | | try { |
| | | if (result.getValue() == null) { |
| | | // nothing has been entered ==> cancel |
| | | return MenuResult.cancel(); |
| | | } |
| | | String argName = CLIProfile.getInstance().getNamingArgument(r); |
| | | StringArgument arg = new StringArgument(argName, null, argName, false, true, |
| | | INFO_NAME_PLACEHOLDER.get(), INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName())); |
| | | if (r instanceof InstantiableRelationDefinition) { |
| | | arg.addValue(result.getValue()); |
| | | } else { |
| | | // Set relation: need the short type name. |
| | | String longName = result.getValue(); |
| | | try { |
| | | AbstractManagedObjectDefinition<?, ?> cd = d.getChild(longName); |
| | | arg.addValue(getShortTypeName(r.getChildDefinition(), cd)); |
| | | } catch (IllegalArgumentException e) { |
| | | arg.addValue(children.get(result.getValue())); |
| | | } |
| | | } |
| | | getCommandBuilder().addArgument(arg); |
| | | } catch (Throwable t) { |
| | | // Bug |
| | | throw new RuntimeException("Unexpected exception: " + t, t); |
| | | } |
| | | return result; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Registers the property name argument with the sub-command. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @throws ArgumentException |
| | | * If the property name argument could not be registered. |
| | | */ |
| | | protected final void registerPropertyNameArgument(SubCommand subCommand) throws ArgumentException { |
| | | this.propertyArgument = new StringArgument(OPTION_DSCFG_LONG_PROPERTY, OPTION_DSCFG_SHORT_PROPERTY, |
| | | OPTION_DSCFG_LONG_PROPERTY, false, true, true, INFO_PROPERTY_PLACEHOLDER.get(), null, null, |
| | | INFO_DSCFG_DESCRIPTION_PROP.get()); |
| | | subCommand.addArgument(propertyArgument); |
| | | } |
| | | |
| | | /** |
| | | * Registers the record mode argument with the sub-command. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @throws ArgumentException |
| | | * If the record mode argument could not be registered. |
| | | */ |
| | | protected final void registerRecordModeArgument(SubCommand subCommand) throws ArgumentException { |
| | | this.recordModeArgument = new BooleanArgument(OPTION_DSCFG_LONG_RECORD, OPTION_DSCFG_SHORT_RECORD, |
| | | OPTION_DSCFG_LONG_RECORD, INFO_DSCFG_DESCRIPTION_RECORD.get()); |
| | | this.recordModeArgument.setPropertyName(OPTION_DSCFG_LONG_RECORD); |
| | | subCommand.addArgument(recordModeArgument); |
| | | } |
| | | |
| | | /** |
| | | * Registers the unit-size argument with the sub-command. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @throws ArgumentException |
| | | * If the unit-size argument could not be registered. |
| | | */ |
| | | protected final void registerUnitSizeArgument(SubCommand subCommand) throws ArgumentException { |
| | | this.unitSizeArgument = new StringArgument(OPTION_DSCFG_LONG_UNIT_SIZE, OPTION_DSCFG_SHORT_UNIT_SIZE, |
| | | OPTION_DSCFG_LONG_UNIT_SIZE, false, true, INFO_UNIT_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_UNIT_SIZE.get()); |
| | | this.unitSizeArgument.setPropertyName(OPTION_DSCFG_LONG_UNIT_SIZE); |
| | | |
| | | subCommand.addArgument(unitSizeArgument); |
| | | } |
| | | |
| | | /** |
| | | * Registers the unit-time argument with the sub-command. |
| | | * |
| | | * @param subCommand |
| | | * The sub-command. |
| | | * @throws ArgumentException |
| | | * If the unit-time argument could not be registered. |
| | | */ |
| | | protected final void registerUnitTimeArgument(SubCommand subCommand) throws ArgumentException { |
| | | this.unitTimeArgument = new StringArgument(OPTION_DSCFG_LONG_UNIT_TIME, OPTION_DSCFG_SHORT_UNIT_TIME, |
| | | OPTION_DSCFG_LONG_UNIT_TIME, false, true, INFO_UNIT_PLACEHOLDER.get(), |
| | | INFO_DSCFG_DESCRIPTION_UNIT_TIME.get()); |
| | | this.unitTimeArgument.setPropertyName(OPTION_DSCFG_LONG_UNIT_TIME); |
| | | |
| | | subCommand.addArgument(unitTimeArgument); |
| | | } |
| | | |
| | | /** |
| | | * Updates the command builder with the arguments defined in the sub command. This implies basically putting the |
| | | * arguments provided by the user in the command builder. |
| | | */ |
| | | protected final void updateCommandBuilderWithSubCommand() { |
| | | for (Argument arg : getSubCommand().getArguments()) { |
| | | if (arg.isPresent()) { |
| | | getCommandBuilder().addArgument(arg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the string value for a given object as it will be displayed in the equivalent command-line. The code will |
| | | * cast the provided object using the property definition. |
| | | * |
| | | * @param propertyDefinition |
| | | * the property definition. |
| | | * @param o |
| | | * the value. |
| | | * @param <T> |
| | | * the type of the property to be retrieved. |
| | | * @return the String value to be displayed in the equivalent command-line. |
| | | */ |
| | | protected static <T> String castAndGetArgumentValue(PropertyDefinition<T> propertyDefinition, Object o) { |
| | | return propertyDefinition.encodeValue(propertyDefinition.castValue(o)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the string value for a given object as it will be displayed in the equivalent command-line. |
| | | * |
| | | * @param propertyDefinition |
| | | * the property definition. |
| | | * @param o |
| | | * the value. |
| | | * @param <T> |
| | | * the type of the property to be retrieved. |
| | | * @return the String value to be displayed in the equivalent command-line. |
| | | */ |
| | | protected static <T> String getArgumentValue(PropertyDefinition<T> propertyDefinition, T o) { |
| | | return propertyDefinition.encodeValue(o); |
| | | } |
| | | |
| | | /** |
| | | * Returns a mapping of subordinate managed object type argument values to their corresponding managed object |
| | | * definitions for the provided managed object definition. |
| | | * |
| | | * @param <C> |
| | | * The type of client configuration. |
| | | * @param <S> |
| | | * The type of server configuration. |
| | | * @param d |
| | | * The managed object definition. |
| | | * @return A mapping of managed object type argument values to their corresponding managed object definitions. |
| | | */ |
| | | protected static <C extends ConfigurationClient, S extends Configuration> SortedMap<String, |
| | | ManagedObjectDefinition<? extends C, ? extends S>> getSubTypes( |
| | | AbstractManagedObjectDefinition<C, S> d) { |
| | | SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> map; |
| | | map = new TreeMap<String, ManagedObjectDefinition<? extends C, ? extends S>>(); |
| | | |
| | | // If the top-level definition is instantiable, we use the value |
| | | // "generic" or "custom". |
| | | if (!d.hasOption(ManagedObjectOption.HIDDEN) && d instanceof ManagedObjectDefinition) { |
| | | ManagedObjectDefinition<? extends C, ? extends S> mod |
| | | = (ManagedObjectDefinition<? extends C, ? extends S>) d; |
| | | map.put(getShortTypeName(d, mod), mod); |
| | | } |
| | | |
| | | // Process its sub-definitions. |
| | | for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d.getAllChildren()) { |
| | | if (c.hasOption(ManagedObjectOption.HIDDEN)) { |
| | | continue; |
| | | } |
| | | |
| | | if (c instanceof ManagedObjectDefinition) { |
| | | @SuppressWarnings("unchecked") |
| | | ManagedObjectDefinition<? extends C, ? extends S> mod |
| | | = (ManagedObjectDefinition<? extends C, ? extends S>) c; |
| | | map.put(getShortTypeName(d, mod), mod); |
| | | } |
| | | } |
| | | |
| | | return map; |
| | | } |
| | | |
| | | /** |
| | | * Returns the type short name for a child managed object definition. |
| | | * |
| | | * @param <C> |
| | | * The type of client configuration. |
| | | * @param <S> |
| | | * The type of server configuration. |
| | | * @param d |
| | | * The top level parent definition. |
| | | * @param c |
| | | * The child definition. |
| | | * @return The type short name. |
| | | */ |
| | | protected static <C extends ConfigurationClient, S extends Configuration> String getShortTypeName( |
| | | AbstractManagedObjectDefinition<C, S> d, AbstractManagedObjectDefinition<?, ?> c) { |
| | | if (c == d) { |
| | | // This is the top-level definition, so use the value "generic" or |
| | | // "custom". |
| | | if (CLIProfile.getInstance().isForCustomization(c)) { |
| | | return DSConfig.CUSTOM_TYPE; |
| | | } else { |
| | | return DSConfig.GENERIC_TYPE; |
| | | } |
| | | } else { |
| | | // It's a child definition. |
| | | String suffix = "-" + d.getName(); |
| | | |
| | | // For the type name we shorten it, if possible, by stripping |
| | | // off the trailing part of the name which matches the |
| | | // base-type. |
| | | String name = c.getName(); |
| | | if (name.endsWith(suffix)) { |
| | | name = name.substring(0, name.length() - suffix.length()); |
| | | } |
| | | |
| | | // If this type is intended for customization, prefix it with |
| | | // "custom". |
| | | if (CLIProfile.getInstance().isForCustomization(c)) { |
| | | name = String.format("%s-%s", DSConfig.CUSTOM_TYPE, name); |
| | | } |
| | | |
| | | return name; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns a usage string representing the list of possible types for the provided managed object definition. |
| | | * |
| | | * @param d |
| | | * The managed object definition. |
| | | * @return A usage string representing the list of possible types for the provided managed object definition. |
| | | */ |
| | | protected static String getSubTypesUsage(AbstractManagedObjectDefinition<?, ?> d) { |
| | | // Build the -t option usage. |
| | | SortedMap<String, ?> types = getSubTypes(d); |
| | | StringBuilder builder = new StringBuilder(); |
| | | boolean isFirst = true; |
| | | for (String s : types.keySet()) { |
| | | if (!isFirst) { |
| | | builder.append(" | "); |
| | | } |
| | | builder.append(s); |
| | | isFirst = false; |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.config.dsconfig; |
| | | |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.opendj.config.AbstractManagedObjectDefinition; |
| | | import org.forgerock.opendj.config.Configuration; |
| | | import org.forgerock.opendj.config.ConfigurationClient; |
| | | import org.forgerock.opendj.config.InstantiableRelationDefinition; |
| | | import org.forgerock.opendj.config.ManagedObjectPath; |
| | | import org.forgerock.opendj.config.OptionalRelationDefinition; |
| | | import org.forgerock.opendj.config.RelationDefinition; |
| | | import org.forgerock.opendj.config.RelationDefinitionVisitor; |
| | | import org.forgerock.opendj.config.RelationOption; |
| | | import org.forgerock.opendj.config.SetRelationDefinition; |
| | | import org.forgerock.opendj.config.SingletonRelationDefinition; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.SubCommandArgumentParser; |
| | | |
| | | /** |
| | | * Uses the administration framework introspection API to construct the dsconfig sub-command handlers. |
| | | */ |
| | | final class SubCommandHandlerFactory { |
| | | |
| | | /** |
| | | * A relation definition visitor used to recursively determine the set of available sub-commands. |
| | | */ |
| | | private final class Visitor implements RelationDefinitionVisitor<Void, ManagedObjectPath<?, ?>> { |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> Void visitInstantiable( |
| | | InstantiableRelationDefinition<C, S> rd, ManagedObjectPath<?, ?> p) { |
| | | try { |
| | | // Create the sub-commands. |
| | | createHandlers.add(CreateSubCommandHandler.create(parser, p, rd)); |
| | | deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, rd)); |
| | | listHandlers.add(ListSubCommandHandler.create(parser, p, rd)); |
| | | getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, rd)); |
| | | setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, rd)); |
| | | |
| | | // Process the referenced managed object definition and its |
| | | // sub-types. |
| | | processRelation(p, rd); |
| | | } catch (ArgumentException e) { |
| | | exception = e; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> Void visitOptional( |
| | | OptionalRelationDefinition<C, S> rd, ManagedObjectPath<?, ?> p) { |
| | | try { |
| | | // Create the sub-commands. |
| | | createHandlers.add(CreateSubCommandHandler.create(parser, p, rd)); |
| | | deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, rd)); |
| | | listHandlers.add(ListSubCommandHandler.create(parser, p, rd)); |
| | | getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, rd)); |
| | | setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, rd)); |
| | | |
| | | // Process the referenced managed object definition and its |
| | | // sub-types. |
| | | processRelation(p, rd); |
| | | } catch (ArgumentException e) { |
| | | exception = e; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> Void visitSet(SetRelationDefinition<C, S> rd, |
| | | ManagedObjectPath<?, ?> p) { |
| | | try { |
| | | // Create the sub-commands. |
| | | createHandlers.add(CreateSubCommandHandler.create(parser, p, rd)); |
| | | deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, rd)); |
| | | listHandlers.add(ListSubCommandHandler.create(parser, p, rd)); |
| | | getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, rd)); |
| | | setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, rd)); |
| | | |
| | | // Process the referenced managed object definition and its |
| | | // sub-types. |
| | | processRelation(p, rd); |
| | | } catch (ArgumentException e) { |
| | | exception = e; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public <C extends ConfigurationClient, S extends Configuration> Void visitSingleton( |
| | | SingletonRelationDefinition<C, S> rd, ManagedObjectPath<?, ?> p) { |
| | | try { |
| | | // Create the sub-commands. |
| | | getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, rd)); |
| | | setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, rd)); |
| | | |
| | | // Process the referenced managed object definition and its |
| | | // sub-types. |
| | | processRelation(p, rd); |
| | | } catch (ArgumentException e) { |
| | | exception = e; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** The set of all available sub-commands. */ |
| | | private final SortedSet<SubCommandHandler> allHandlers = new TreeSet<SubCommandHandler>(); |
| | | |
| | | /** The set of create-xxx available sub-commands. */ |
| | | private final SortedSet<CreateSubCommandHandler<?, ?>> createHandlers |
| | | = new TreeSet<CreateSubCommandHandler<?, ?>>(); |
| | | |
| | | /** The set of delete-xxx available sub-commands. */ |
| | | private final SortedSet<DeleteSubCommandHandler> deleteHandlers = new TreeSet<DeleteSubCommandHandler>(); |
| | | |
| | | /** Any exception that occurred whilst creating the sub-commands. */ |
| | | private ArgumentException exception = null; |
| | | |
| | | /** The set of get-xxx-prop available sub-commands. */ |
| | | private final SortedSet<GetPropSubCommandHandler> getPropHandlers = new TreeSet<GetPropSubCommandHandler>(); |
| | | |
| | | /** The help sub-command handler. */ |
| | | private HelpSubCommandHandler helpHandler = null; |
| | | |
| | | /** The set of list-xxx available sub-commands. */ |
| | | private final SortedSet<ListSubCommandHandler> listHandlers = new TreeSet<ListSubCommandHandler>(); |
| | | |
| | | /** The sub-command argument parser. */ |
| | | private final SubCommandArgumentParser parser; |
| | | |
| | | /** The set of set-xxx-prop available sub-commands. */ |
| | | private final SortedSet<SetPropSubCommandHandler> setPropHandlers = new TreeSet<SetPropSubCommandHandler>(); |
| | | |
| | | /** The relation visitor. */ |
| | | private final Visitor visitor = new Visitor(); |
| | | |
| | | /** |
| | | * Create a new sub-command builder. |
| | | * |
| | | * @param parser |
| | | * The sub-command argument parser. |
| | | * @throws ArgumentException |
| | | * If a sub-command could not be created successfully. |
| | | */ |
| | | public SubCommandHandlerFactory(SubCommandArgumentParser parser) throws ArgumentException { |
| | | this.parser = parser; |
| | | |
| | | // We always need a help properties sub-command handler. |
| | | helpHandler = HelpSubCommandHandler.create(parser); |
| | | |
| | | processPath(ManagedObjectPath.emptyPath()); |
| | | |
| | | allHandlers.add(helpHandler); |
| | | allHandlers.addAll(createHandlers); |
| | | allHandlers.addAll(deleteHandlers); |
| | | allHandlers.addAll(listHandlers); |
| | | allHandlers.addAll(getPropHandlers); |
| | | allHandlers.addAll(setPropHandlers); |
| | | |
| | | if (exception != null) { |
| | | throw exception; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Gets all the sub-command handlers. |
| | | * |
| | | * @return Returns all the sub-command handlers. |
| | | */ |
| | | public SortedSet<SubCommandHandler> getAllSubCommandHandlers() { |
| | | return allHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Gets all the create-xxx sub-command handlers. |
| | | * |
| | | * @return Returns all the create-xxx sub-command handlers. |
| | | */ |
| | | public SortedSet<CreateSubCommandHandler<?, ?>> getCreateSubCommandHandlers() { |
| | | return createHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Gets all the delete-xxx sub-command handlers. |
| | | * |
| | | * @return Returns all the delete-xxx sub-command handlers. |
| | | */ |
| | | public SortedSet<DeleteSubCommandHandler> getDeleteSubCommandHandlers() { |
| | | return deleteHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Gets all the get-xxx-prop sub-command handlers. |
| | | * |
| | | * @return Returns all the get-xxx-prop sub-command handlers. |
| | | */ |
| | | public SortedSet<GetPropSubCommandHandler> getGetPropSubCommandHandlers() { |
| | | return getPropHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Gets all the list-xxx sub-command handlers. |
| | | * |
| | | * @return Returns all the list-xxx sub-command handlers. |
| | | */ |
| | | public SortedSet<ListSubCommandHandler> getListSubCommandHandlers() { |
| | | return listHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Gets all the set-xxx-prop sub-command handlers. |
| | | * |
| | | * @return Returns all the set-xxx-prop sub-command handlers. |
| | | */ |
| | | public SortedSet<SetPropSubCommandHandler> getSetPropSubCommandHandlers() { |
| | | return setPropHandlers; |
| | | } |
| | | |
| | | /** |
| | | * Process the relations associated with the managed object definition identified by the provided path. |
| | | */ |
| | | private void processPath(ManagedObjectPath<?, ?> path) { |
| | | AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition(); |
| | | |
| | | // Do not process inherited relation definitions. |
| | | for (RelationDefinition<?, ?> r : d.getRelationDefinitions()) { |
| | | if (!r.hasOption(RelationOption.HIDDEN)) { |
| | | r.accept(visitor, path); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** Process an instantiable relation. */ |
| | | private <C extends ConfigurationClient, S extends Configuration> void processRelation(ManagedObjectPath<?, ?> path, |
| | | InstantiableRelationDefinition<C, S> r) { |
| | | AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition(); |
| | | |
| | | // Process all relations associated directly with this |
| | | // definition. |
| | | helpHandler.registerManagedObjectDefinition(d); |
| | | processPath(path.child(r, d, "DUMMY")); |
| | | |
| | | // Now process relations associated with derived definitions. |
| | | for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d.getAllChildren()) { |
| | | helpHandler.registerManagedObjectDefinition(c); |
| | | processPath(path.child(r, c, "DUMMY")); |
| | | } |
| | | } |
| | | |
| | | /** Process an optional relation. */ |
| | | private <C extends ConfigurationClient, S extends Configuration> void processRelation(ManagedObjectPath<?, ?> path, |
| | | OptionalRelationDefinition<C, S> r) { |
| | | AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition(); |
| | | |
| | | // Process all relations associated directly with this |
| | | // definition. |
| | | helpHandler.registerManagedObjectDefinition(d); |
| | | processPath(path.child(r, d)); |
| | | |
| | | // Now process relations associated with derived definitions. |
| | | for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d.getAllChildren()) { |
| | | helpHandler.registerManagedObjectDefinition(c); |
| | | processPath(path.child(r, c)); |
| | | } |
| | | } |
| | | |
| | | /** Process a set relation. */ |
| | | private <C extends ConfigurationClient, S extends Configuration> void processRelation(ManagedObjectPath<?, ?> path, |
| | | SetRelationDefinition<C, S> r) { |
| | | AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition(); |
| | | |
| | | // Process all relations associated directly with this |
| | | // definition. |
| | | helpHandler.registerManagedObjectDefinition(d); |
| | | processPath(path.child(r, d)); |
| | | |
| | | // Now process relations associated with derived definitions. |
| | | for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d.getAllChildren()) { |
| | | helpHandler.registerManagedObjectDefinition(c); |
| | | processPath(path.child(r, c)); |
| | | } |
| | | } |
| | | |
| | | /** Process a singleton relation. */ |
| | | private <C extends ConfigurationClient, S extends Configuration> void processRelation(ManagedObjectPath<?, ?> path, |
| | | SingletonRelationDefinition<C, S> r) { |
| | | AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition(); |
| | | |
| | | // Process all relations associated directly with this |
| | | // definition. |
| | | helpHandler.registerManagedObjectDefinition(d); |
| | | processPath(path.child(r, d)); |
| | | |
| | | // Now process relations associated with derived definitions. |
| | | for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d.getAllChildren()) { |
| | | helpHandler.registerManagedObjectDefinition(c); |
| | | processPath(path.child(r, c)); |
| | | } |
| | | } |
| | | } |
| | |
| | | WARN_CONFIG_LOGGING_UNSUPPORTED_FIELDS_IN_LOG_FORMAT_734=The log format \ |
| | | for %s contains the folowing unsupported fields: %s. Their output will be \ |
| | | replaced with a dash ("-") character |
| | | ERR_BUILDVERSION_MISMATCH_735=The OpenDJ binary version '%s' does not match the installed \ |
| | | version '%s'. Please run upgrade before continuing |
| | | ERR_BUILDVERSION_MALFORMED_736=The version of the installed OpenDJ could not be determined \ |
| | | because the version file '%s' exists but contains invalid data. \ |
| | | Restore it from backup before continuing |
| | | ERR_BUILDVERSION_NOT_FOUND_737=The version of the installed OpenDJ could not be determined \ |
| | | because the version file '%s' could not be found. Restore it from backup before continuing |
| | | ERR_CONFIGVERSION_NOT_FOUND_738=The version of the installed OpenDJ could not be determined \ |
| | | because tan error occurs during reading the current configuration. |