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 Sun Microsystems, Inc.
015 * Portions Copyright 2015-2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.config.client;
018
019import static com.forgerock.opendj.ldap.config.ConfigMessages.*;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024
025import org.forgerock.i18n.LocalizableMessage;
026import org.forgerock.i18n.LocalizableMessageBuilder;
027import org.forgerock.util.Reject;
028
029/**
030 * This exception is thrown when the client or server refuses to create, delete,
031 * or modify a managed object due to one or more constraints that cannot be
032 * satisfied.
033 * <p>
034 * Operations can be rejected either by a client-side constraint violation
035 * triggered by {@link ClientConstraintHandler}, or by a server-side error.
036 * <p>
037 * For example, the Directory Server might not be able perform an operation due
038 * to some OS related problem, such as lack of disk space, or missing files.
039 */
040public class OperationRejectedException extends AdminClientException {
041
042    /** The type of operation that caused this exception. */
043    public enum OperationType {
044        /** A managed object could not be created. */
045        CREATE,
046        /** A managed object could not be deleted. */
047        DELETE,
048        /** A managed object could not be modified. */
049        MODIFY;
050    }
051
052    /** Serialization ID. */
053    private static final long serialVersionUID = 8547688890613079044L;
054
055    /** Gets the default message. */
056    private static LocalizableMessage getDefaultMessage(Collection<LocalizableMessage> messages) {
057        Reject.ifNull(messages);
058        Reject.ifFalse(!messages.isEmpty(), "Messages should not be empty");
059
060        if (messages.size() == 1) {
061            return ERR_OPERATION_REJECTED_EXCEPTION_SINGLE.get(messages.iterator().next());
062        } else {
063            return ERR_OPERATION_REJECTED_EXCEPTION_PLURAL.get(getSingleMessage(messages));
064        }
065    }
066
067    /** Merge the messages into a single message. */
068    private static LocalizableMessage getSingleMessage(Collection<LocalizableMessage> messages) {
069        if (messages.size() == 1) {
070            return messages.iterator().next();
071        } else {
072            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
073
074            boolean isFirst = true;
075            for (LocalizableMessage m : messages) {
076                if (!isFirst) {
077                    builder.append(";  ");
078                }
079                builder.append(m);
080                isFirst = false;
081            }
082
083            return builder.toMessage();
084        }
085    }
086
087    /** The messages describing the constraint violations that occurred. */
088    private final Collection<LocalizableMessage> messages;
089
090    /** The type of operation that caused this exception. */
091    private final OperationType type;
092
093    /** The user friendly name of the component that caused this exception. */
094    private final LocalizableMessage ufn;
095
096    /**
097     * Creates a new operation rejected exception with a default message.
098     *
099     * @param type
100     *            The type of operation that caused this exception.
101     * @param ufn
102     *            The user friendly name of the component that caused this
103     *            exception.
104     */
105    public OperationRejectedException(OperationType type, LocalizableMessage ufn) {
106        this(type, ufn, ERR_OPERATION_REJECTED_DEFAULT.get());
107    }
108
109    /**
110     * Creates a new operation rejected exception with the provided messages.
111     *
112     * @param type
113     *            The type of operation that caused this exception.
114     * @param ufn
115     *            The user friendly name of the component that caused this
116     *            exception.
117     * @param messages
118     *            The messages describing the constraint violations that
119     *            occurred (must be non-<code>null</code> and non-empty).
120     */
121    public OperationRejectedException(OperationType type, LocalizableMessage ufn,
122        Collection<LocalizableMessage> messages) {
123        super(getDefaultMessage(messages));
124
125        this.messages = new ArrayList<>(messages);
126        this.type = type;
127        this.ufn = ufn;
128    }
129
130    /**
131     * Creates a new operation rejected exception with the provided message.
132     *
133     * @param type
134     *            The type of operation that caused this exception.
135     * @param ufn
136     *            The user friendly name of the component that caused this
137     *            exception.
138     * @param message
139     *            The message describing the constraint violation that occurred.
140     */
141    public OperationRejectedException(OperationType type, LocalizableMessage ufn, LocalizableMessage message) {
142        this(type, ufn, Collections.singleton(message));
143    }
144
145    /**
146     * Gets an unmodifiable collection view of the messages describing the
147     * constraint violations that occurred.
148     *
149     * @return Returns an unmodifiable collection view of the messages
150     *         describing the constraint violations that occurred.
151     */
152    public Collection<LocalizableMessage> getMessages() {
153        return Collections.unmodifiableCollection(messages);
154    }
155
156    /**
157     * Creates a single message listing all the messages combined into a single
158     * list separated by semi-colons.
159     *
160     * @return Returns a single message listing all the messages combined into a
161     *         single list separated by semi-colons.
162     */
163    public LocalizableMessage getMessagesAsSingleMessage() {
164        return getSingleMessage(messages);
165    }
166
167    /**
168     * Gets the type of operation that caused this exception.
169     *
170     * @return Returns the type of operation that caused this exception.
171     */
172    public OperationType getOperationType() {
173        return type;
174    }
175
176    /**
177     * Gets the user friendly name of the component that caused this exception.
178     *
179     * @return Returns the user friendly name of the component that caused this
180     *         exception.
181     */
182    public LocalizableMessage getUserFriendlyName() {
183        return ufn;
184    }
185
186}