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