001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014 ForgeRock AS
026 */
027package org.forgerock.opendj.config.dsconfig;
028
029import static com.forgerock.opendj.dsconfig.DsconfigMessages.*;
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
033import org.forgerock.opendj.config.ManagedObjectDefinition;
034import org.forgerock.opendj.config.PropertyDefinition;
035import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder;
036import org.forgerock.opendj.config.PropertyException;
037import org.forgerock.opendj.config.RelationDefinition;
038import org.forgerock.opendj.config.client.IllegalManagedObjectNameException;
039import org.forgerock.opendj.config.client.ManagedObjectDecodingException;
040import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException;
041import org.forgerock.opendj.config.client.OperationRejectedException;
042
043import com.forgerock.opendj.cli.Argument;
044import com.forgerock.opendj.cli.ArgumentException;
045import com.forgerock.opendj.cli.ClientException;
046import com.forgerock.opendj.cli.ConsoleApplication;
047import com.forgerock.opendj.cli.ReturnCode;
048import com.forgerock.opendj.cli.TableBuilder;
049import com.forgerock.opendj.cli.TextTablePrinter;
050
051/**
052 * A utility class for converting various admin exception types into argument exceptions.
053 */
054public final class ArgumentExceptionFactory {
055
056    /**
057     * Creates a ClientException exception from an illegal managed object name exception.
058     *
059     * @param e
060     *            The illegal managed object name exception.
061     * @param d
062     *            The managed object definition.
063     * @return Returns a ClientException exception.
064     */
065    public static ClientException adaptIllegalManagedObjectNameException(IllegalManagedObjectNameException e,
066            AbstractManagedObjectDefinition<?, ?> d) {
067        String illegalName = e.getIllegalName();
068        PropertyDefinition<?> pd = e.getNamingPropertyDefinition();
069
070        if (illegalName.length() == 0) {
071            LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_EMPTY.get(d.getUserFriendlyPluralName());
072            return new ClientException(ReturnCode.ERROR_USER_DATA, message);
073        } else if (illegalName.trim().length() == 0) {
074            LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_BLANK.get(d.getUserFriendlyPluralName());
075            return new ClientException(ReturnCode.ERROR_USER_DATA, message);
076        } else if (pd != null) {
077            try {
078                pd.decodeValue(illegalName);
079            } catch (PropertyException e1) {
080                PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true);
081                LocalizableMessage syntax = b.getUsage(pd);
082
083                LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_SYNTAX.get(illegalName,
084                        d.getUserFriendlyName(), syntax);
085                return new ClientException(ReturnCode.ERROR_USER_DATA, message);
086            }
087        }
088
089        LocalizableMessage message = ERR_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN.get(illegalName, d.getUserFriendlyName());
090        return new ClientException(ReturnCode.ERROR_USER_DATA, message);
091    }
092
093    /**
094     * Creates an argument exception from a property exception.
095     *
096     * @param e
097     *            The property exception.
098     * @param d
099     *            The managed object definition.
100     * @return Returns an argument exception.
101     */
102    public static ArgumentException adaptPropertyException(PropertyException e,
103            AbstractManagedObjectDefinition<?, ?> d) {
104        return new ArgumentException(e.getMessageObject());
105    }
106
107    /**
108     * Displays a table listing reasons why a managed object could not be decoded successfully.
109     *
110     * @param app
111     *            The console application.
112     * @param e
113     *            The managed object decoding exception.
114     */
115    public static void displayManagedObjectDecodingException(ConsoleApplication app, ManagedObjectDecodingException e) {
116        AbstractManagedObjectDefinition<?, ?> d = e.getPartialManagedObject().getManagedObjectDefinition();
117        LocalizableMessage ufn = d.getUserFriendlyName();
118        LocalizableMessage msg = e.getCauses().size() == 1 ? ERR_GET_HEADING_MODE_SINGLE.get(ufn)
119                                                           : ERR_GET_HEADING_MODE_PLURAL.get(ufn);
120        app.errPrintln(msg);
121        app.errPrintln();
122        TableBuilder builder = new TableBuilder();
123        for (PropertyException pe : e.getCauses()) {
124            ArgumentException ae = adaptPropertyException(pe, d);
125            builder.startRow();
126            builder.appendCell("*");
127            builder.appendCell(ae.getMessage());
128        }
129
130        TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
131        printer.setDisplayHeadings(false);
132        printer.setColumnWidth(1, 0);
133        printer.setIndentWidth(4);
134        builder.print(printer);
135    }
136
137    /**
138     * Displays a table listing missing mandatory properties.
139     *
140     * @param app
141     *            The console application.
142     * @param e
143     *            The missing mandatory property exception.
144     */
145    public static void displayMissingMandatoryPropertyException(ConsoleApplication app,
146            MissingMandatoryPropertiesException e) {
147        LocalizableMessage ufn = e.getUserFriendlyName();
148        LocalizableMessage msg;
149        final boolean onePropertyMissing = e.getCauses().size() == 1;
150        if (e.isCreate()) {
151            msg = onePropertyMissing ? ERR_CREATE_HEADING_MMPE_SINGLE.get(ufn)
152                                     : ERR_CREATE_HEADING_MMPE_PLURAL.get(ufn);
153        } else {
154            msg = onePropertyMissing ? ERR_MODIFY_HEADING_MMPE_SINGLE.get(ufn)
155                                     : ERR_MODIFY_HEADING_MMPE_PLURAL.get(ufn);
156        }
157
158        app.errPrintln(msg);
159        app.errPrintln();
160        TableBuilder builder = new TableBuilder();
161        builder.addSortKey(0);
162        builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get());
163        builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get());
164
165        PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true);
166        for (PropertyException pe : e.getCauses()) {
167            PropertyDefinition<?> pd = pe.getPropertyDefinition();
168            builder.startRow();
169            builder.appendCell(pd.getName());
170            builder.appendCell(b.getUsage(pd));
171        }
172
173        TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
174        printer.setDisplayHeadings(true);
175        printer.setColumnWidth(1, 0);
176        printer.setIndentWidth(4);
177        builder.print(printer);
178    }
179
180    /**
181     * Displays a table listing the reasons why an operation was rejected.
182     *
183     * @param app
184     *            The console application.
185     * @param e
186     *            The operation rejected exception.
187     */
188    public static void displayOperationRejectedException(ConsoleApplication app, OperationRejectedException e) {
189        LocalizableMessage ufn = e.getUserFriendlyName();
190        LocalizableMessage msg;
191        final boolean singleMessage = e.getMessages().size() == 1;
192
193        switch (e.getOperationType()) {
194        case CREATE:
195            msg = singleMessage ? ERR_DSCFG_ERROR_CREATE_ORE_SINGLE.get(ufn)
196                                : ERR_DSCFG_ERROR_CREATE_ORE_PLURAL.get(ufn);
197            break;
198        case DELETE:
199            msg = singleMessage ? ERR_DSCFG_ERROR_DELETE_ORE_SINGLE.get(ufn)
200                                : ERR_DSCFG_ERROR_DELETE_ORE_PLURAL.get(ufn);
201            break;
202        default:
203            msg = singleMessage ? ERR_DSCFG_ERROR_MODIFY_ORE_SINGLE.get(ufn)
204                                : ERR_DSCFG_ERROR_MODIFY_ORE_PLURAL.get(ufn);
205            break;
206        }
207
208        app.errPrintln(msg);
209        app.errPrintln();
210        TableBuilder builder = new TableBuilder();
211        for (LocalizableMessage reason : e.getMessages()) {
212            builder.startRow();
213            builder.appendCell("*");
214            builder.appendCell(reason);
215        }
216        TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
217        printer.setDisplayHeadings(false);
218        printer.setColumnWidth(1, 0);
219        printer.setIndentWidth(4);
220        builder.print(printer);
221    }
222
223    /**
224     * Creates an argument exception which should be used when a property modification argument is incompatible with a
225     * previous modification argument.
226     *
227     * @param arg
228     *            The incompatible argument.
229     * @return Returns an argument exception.
230     */
231    public static ArgumentException incompatiblePropertyModification(String arg) {
232        LocalizableMessage msg = ERR_DSCFG_ERROR_INCOMPATIBLE_PROPERTY_MOD.get(arg);
233        return new ArgumentException(msg);
234    }
235
236    /**
237     * Creates an argument exception which should be used when the client has not specified a bind password.
238     *
239     * @param bindDN
240     *            The name of the user requiring a password.
241     * @return Returns an argument exception.
242     */
243    public static ArgumentException missingBindPassword(String bindDN) {
244        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_PASSWORD.get(bindDN);
245        return new ArgumentException(msg);
246    }
247
248    /**
249     * Creates an argument exception which should be used when the client has not specified a bind password.
250     *
251     * @param bindDN
252     *            The name of the user requiring a password.
253     * @return Returns an argument exception.
254     */
255    public static ArgumentException missingBindPassword(char[] bindDN) {
256        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_PASSWORD.get(bindDN);
257        return new ArgumentException(msg);
258    }
259
260    /**
261     * Creates an argument exception which should be used when an argument, which is mandatory when the application is
262     * non-interactive, has not been specified.
263     *
264     * @param arg
265     *            The missing argument.
266     * @return Returns an argument exception.
267     */
268    public static ArgumentException missingMandatoryNonInteractiveArgument(Argument arg) {
269        LocalizableMessage msg = ERR_DSCFG_ERROR_MISSING_NON_INTERACTIVE_ARG.get(arg.getLongIdentifier());
270        return new ArgumentException(msg);
271    }
272
273    /**
274     * Creates an argument exception which should be used when a property value argument is invalid because it does not
275     * a property name.
276     *
277     * @param arg
278     *            The argument having the missing property name.
279     * @return Returns an argument exception.
280     */
281    public static ArgumentException missingNameInPropertyArgument(String arg) {
282        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_VALUE.get(arg);
283        return new ArgumentException(msg);
284    }
285
286    /**
287     * Creates an argument exception which should be used when a property modification argument is invalid because it
288     * does not a property name.
289     *
290     * @param arg
291     *            The argument having the missing property name.
292     * @return Returns an argument exception.
293     */
294    public static ArgumentException missingNameInPropertyModification(String arg) {
295        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD.get(arg);
296        return new ArgumentException(msg);
297    }
298
299    /**
300     * Creates an argument exception which should be used when a property value argument is invalid because it does not
301     * contain a separator between the property name and its value.
302     *
303     * @param arg
304     *            The argument having a missing separator.
305     * @return Returns an argument exception.
306     */
307    public static ArgumentException missingSeparatorInPropertyArgument(String arg) {
308        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_VALUE.get(arg);
309        return new ArgumentException(msg);
310    }
311
312    /**
313     * Creates an argument exception which should be used when a property modification argument is invalid because it
314     * does not contain a separator between the property name and its value.
315     *
316     * @param arg
317     *            The argument having a missing separator.
318     * @return Returns an argument exception.
319     */
320    public static ArgumentException missingSeparatorInPropertyModification(String arg) {
321        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_MOD.get(arg);
322        return new ArgumentException(msg);
323    }
324
325    /**
326     * Creates an argument exception which should be used when a property value argument is invalid because it does not
327     * a property value.
328     *
329     * @param arg
330     *            The argument having the missing property value.
331     * @return Returns an argument exception.
332     */
333    public static ArgumentException missingValueInPropertyArgument(String arg) {
334        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_VALUE.get(arg);
335        return new ArgumentException(msg);
336    }
337
338    /**
339     * Creates an argument exception which should be used when a property modification argument is invalid because it
340     * does not a property value.
341     *
342     * @param arg
343     *            The argument having the missing property value.
344     * @return Returns an argument exception.
345     */
346    public static ArgumentException missingValueInPropertyModification(String arg) {
347        LocalizableMessage msg = ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD.get(arg);
348        return new ArgumentException(msg);
349    }
350
351    /**
352     * Creates an argument exception which should be used when the connection parameters could not be read from the
353     * standard input.
354     *
355     * @param cause
356     *            The reason why the connection parameters could not be read.
357     * @return Returns an argument exception.
358     */
359    public static ArgumentException unableToReadConnectionParameters(Exception cause) {
360        LocalizableMessage message = ERR_DSCFG_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(cause.getMessage());
361        return new ArgumentException(message, cause);
362    }
363
364    /**
365     * Creates an argument exception which should be used when the bind password could not be read from the standard
366     * input because the application is non-interactive.
367     *
368     * @return Returns an argument exception.
369     */
370    public static ArgumentException unableToReadBindPasswordInteractively() {
371        LocalizableMessage message = ERR_DSCFG_ERROR_BIND_PASSWORD_NONINTERACTIVE.get();
372        return new ArgumentException(message);
373    }
374
375    /**
376     * Creates an argument exception which should be used when an attempt is made to reset a mandatory property that
377     * does not have any default values.
378     *
379     * @param d
380     *            The managed object definition.
381     * @param name
382     *            The name of the mandatory property.
383     * @param setOption
384     *            The name of the option which should be used to set the property's values.
385     * @return Returns an argument exception.
386     */
387    public static ArgumentException unableToResetMandatoryProperty(AbstractManagedObjectDefinition<?, ?> d,
388            String name, String setOption) {
389        LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_RESET_MANDATORY_PROPERTY.get(
390                d.getUserFriendlyPluralName(), name, setOption);
391        return new ArgumentException(message);
392    }
393
394    /**
395     * Creates an argument exception which should be used when an attempt is made to reset a property with a value.
396     *
397     * @param name
398     *            The name of the mandatory property.
399     * @param resetOption
400     *            The name of the option which should be used to reset the property's values.
401     * @return Returns an argument exception.
402     */
403    public static ArgumentException unableToResetPropertyWithValue(String name, String resetOption) {
404        LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_RESET_PROPERTY_WITH_VALUE.get(resetOption, name,
405                resetOption);
406        return new ArgumentException(message);
407    }
408
409    /**
410     * Creates an argument exception which should be used when an attempt is made to set the naming property for a
411     * managed object during creation.
412     *
413     * @param d
414     *            The managed object definition.
415     * @param pd
416     *            The naming property definition.
417     * @return Returns an argument exception.
418     */
419    public static ArgumentException unableToSetNamingProperty(AbstractManagedObjectDefinition<?, ?> d,
420            PropertyDefinition<?> pd) {
421        LocalizableMessage message = ERR_DSCFG_ERROR_UNABLE_TO_SET_NAMING_PROPERTY.get(pd.getName(),
422                d.getUserFriendlyName());
423        return new ArgumentException(message);
424    }
425
426    /**
427     * Creates an argument exception which should be used when a component category argument is not recognized.
428     *
429     * @param categoryName
430     *            The unrecognized component category.
431     * @return Returns an argument exception.
432     */
433    public static ArgumentException unknownCategory(String categoryName) {
434        LocalizableMessage msg = ERR_DSCFG_ERROR_CATEGORY_UNRECOGNIZED.get(categoryName);
435        return new ArgumentException(msg);
436    }
437
438    /**
439     * Creates an argument exception which should be used when a property name is not recognized.
440     *
441     * @param d
442     *            The managed object definition.
443     * @param name
444     *            The unrecognized property name.
445     * @return Returns an argument exception.
446     */
447    public static ArgumentException unknownProperty(AbstractManagedObjectDefinition<?, ?> d, String name) {
448        LocalizableMessage message = ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED.get(name, d.getUserFriendlyPluralName());
449        return new ArgumentException(message);
450    }
451
452    /**
453     * Creates an argument exception which should be used when a property name is not recognized.
454     *
455     * @param name
456     *            The unrecognized property name.
457     * @return Returns an argument exception.
458     */
459    public static ArgumentException unknownProperty(String name) {
460        LocalizableMessage message = ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN.get(name);
461        return new ArgumentException(message);
462    }
463
464    /**
465     * Creates an argument exception which should be used when a sub-type argument in a create-xxx sub-command is not
466     * recognized.
467     *
468     * @param r
469     *            The relation definition.
470     * @param typeName
471     *            The unrecognized property sub-type.
472     * @param typeUsage
473     *            A usage string describing the allowed sub-types.
474     * @return Returns an argument exception.
475     */
476    public static ArgumentException unknownSubType(RelationDefinition<?, ?> r, String typeName, String typeUsage) {
477        LocalizableMessage msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED
478                .get(typeName, r.getUserFriendlyName(), typeUsage);
479        return new ArgumentException(msg);
480    }
481
482    /**
483     * Creates an argument exception which should be used when a managed object type argument is not associated with a
484     * category.
485     *
486     * @param categoryName
487     *            The component category.
488     * @param typeName
489     *            The unrecognized component type.
490     * @return Returns an argument exception.
491     */
492    public static ArgumentException unknownTypeForCategory(String typeName, String categoryName) {
493        LocalizableMessage msg = ERR_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED.get(typeName, categoryName);
494        return new ArgumentException(msg);
495    }
496
497    /**
498     * Creates an argument exception which should be used when a multi-valued property does not contain a given value.
499     *
500     * @param value
501     *            The property value.
502     * @param propertyName
503     *            The property name.
504     * @return Returns an argument exception.
505     */
506    public static ArgumentException unknownValueForMultiValuedProperty(String value, String propertyName) {
507        LocalizableMessage msg = ERR_DSCFG_ERROR_VALUE_DOES_NOT_EXIST.get(value, propertyName);
508        return new ArgumentException(msg);
509    }
510
511    /**
512     * Creates an argument exception which should be used when a child component does not exist.
513     *
514     * @param componentName
515     *            The component name.
516     * @return Returns an argument exception.
517     */
518    public static ArgumentException unknownValueForChildComponent(String componentName) {
519        LocalizableMessage msg = ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(componentName);
520        return new ArgumentException(msg);
521    }
522
523    /**
524     * Creates a CLI exception which should be used when a managed object is retrieved but does not have the correct
525     * type appropriate for the associated sub-command.
526     *
527     * @param r
528     *            The relation definition.
529     * @param d
530     *            The definition of the managed object that was retrieved.
531     * @param subcommandName
532     *            the sub-command name.
533     * @return Returns a Client exception.
534     */
535    public static ClientException wrongManagedObjectType(RelationDefinition<?, ?> r, ManagedObjectDefinition<?, ?> d,
536            String subcommandName) {
537        LocalizableMessage msg = ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED_FOR_SUBCOMMAND.get(d.getUserFriendlyName(),
538                subcommandName);
539        return new ClientException(ReturnCode.ERROR_USER_DATA, msg);
540    }
541
542    /** Prevent instantiation. */
543    private ArgumentExceptionFactory() {
544        // No implementation required.
545    }
546}