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