001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AuthLoginException.java,v 1.4 2008/06/25 05:42:06 qcheng Exp $
026 *
027 */
028
029/*
030 * Portions Copyright 2011-2013 ForgeRock AS
031 */
032package com.sun.identity.authentication.spi;
033
034import com.sun.identity.shared.locale.AMResourceBundleCache;
035import com.sun.identity.shared.locale.L10NMessage;
036import java.text.MessageFormat;
037import java.util.Locale;
038import java.util.ResourceBundle;
039import javax.security.auth.login.LoginException;
040
041/**
042 * This class is for handling message localization in LoginException.
043 *
044 * @supported.api
045 */
046public class AuthLoginException extends LoginException implements L10NMessage {
047
048    private static AMResourceBundleCache amCache = AMResourceBundleCache.getInstance();
049
050    private String _bundleName = null;
051
052    private String _errorCode = null;
053
054    private Object[] _args = null;
055
056    private ResourceBundle _bundle = null;
057
058    /**
059     * Constructs an exception with given message and the nested exception.
060     *
061     * @param message
062     *            message of this exception
063     * @param nestedException
064     *            Exception caught by the code block throwing this exception
065     */
066    public AuthLoginException(String message, Throwable nestedException) {
067        super(message);
068        initCause(nestedException);
069    }
070
071    /**
072     * Constructs an <code>AuthLoginException</code> with given
073     * <code>Throwable</code>.
074     *
075     * @param nestedException
076     *            Exception nested in the new exception.
077     *
078     * @supported.api
079     */
080    public AuthLoginException(Throwable nestedException) {
081        initCause(nestedException);
082        if (nestedException instanceof L10NMessage) {
083            _errorCode = ((L10NMessage) nestedException).getErrorCode();
084        }
085    }
086
087    /**
088     * Constructs a new <code>AuthLoginException</code> with the given
089     * message.
090     *
091     * @param message
092     *            message for this exception. This message can be later
093     *            retrieved by <code>getMessage()</code> method.
094     *
095     * @supported.api
096     */
097    public AuthLoginException(String message) {
098        super(message);
099    }
100
101    /**
102     * Constructs an instance of <code> AuthLoginException </code> to pass the
103     * localized error message At this level, the locale of the caller is not
104     * known and it is not possible to throw localized error message at this
105     * level. Instead this constructor provides Resource Bundle name and
106     * <code>errorCode</code> for correctly locating the error message. The
107     * default <code>getMessage()</code> will always return English messages
108     * only. This is consistent with current JRE.
109     *
110     * @param rbName
111     *            Resource Bundle Name to be used for getting localized error
112     *            message.
113     * @param errorCode
114     *            Key to resource bundle. You can use
115     *
116     * <pre>
117     *  ResourceBundle rb = ResourceBunde.getBundle (rbName,locale);
118     *  String localizedStr = rb.getString(errorCode)
119     * </pre>
120     *
121     * @param args
122     *            arguments to message. If it is not present pass them as null
123     * @param nestedException
124     *            The nested <code>Throwable</code>.
125     *
126     * @supported.api
127     */
128    public AuthLoginException(String rbName, String errorCode, Object[] args,
129            Throwable nestedException) {
130        initCause(nestedException);
131        _bundleName = rbName;
132        _errorCode = errorCode;
133        _args = args;
134
135    }
136
137    /**
138     * Constructs a new <code>AuthLoginException</code> without a nested
139     * <code>Throwable</code>.
140     *
141     * @param rbName
142     *            Resource Bundle Name to be used for getting localized error
143     *            message.
144     * @param errorCode
145     *            Key to resource bundle. You can use
146     *
147     * <pre>
148     *  ResourceBundle rb = ResourceBunde.getBundle (rbName,locale);
149     *  String localizedStr = rb.getString(errorCode)
150     * </pre>
151     *
152     * @param args
153     *            arguments to message. If it is not present pass them as null
154     *
155     * @supported.api
156     */
157    public AuthLoginException(String rbName, String errorCode, Object[] args) {
158        this(rbName, errorCode, args, null);
159    }
160
161    /**
162     * Returns the localized message of the given locale.
163     *
164     * @param locale
165     *            the locale in which the message will be returned.
166     * @return String localized error message.
167     *
168     * @supported.api
169     */
170    public String getL10NMessage(Locale locale) {
171        String result = super.getMessage();
172
173        if (_bundleName != null && locale != null && _errorCode != null) {
174            _bundle = amCache.getResBundle(_bundleName, locale);
175            String mid = _bundle.getString(_errorCode);
176            if (_args == null || _args.length == 0) {
177                result = mid;
178            } else {
179                result = MessageFormat.format(mid, _args);
180            }
181        }
182        String chainedMessage = null;
183        Throwable nestedException = getCause();
184        if (nestedException != null) {
185            if (nestedException instanceof L10NMessage) {
186                L10NMessage lex = (L10NMessage) nestedException;
187                chainedMessage = lex.getL10NMessage(locale);
188            } else {
189                chainedMessage = nestedException.getMessage();
190            }
191        }
192        if (result == null) {
193            result = chainedMessage;
194        } else if (chainedMessage != null) {
195            result = result + "\n" + chainedMessage;
196        }
197        return result;
198    }
199
200    /**
201     * Returns the resource bundle name.
202     *
203     * @return Resource Bundle Name associated with this error message.
204     * @see #getL10NMessage(java.util.Locale).
205     *
206     * @supported.api
207     */
208    public String getResourceBundleName() {
209        return _bundleName;
210    }
211
212    /**
213     * Returns the error code.
214     *
215     * @return Error code associated with this error message.
216     *
217     * @supported.api
218     */
219    public String getErrorCode() {
220        return _errorCode;
221    }
222
223    /**
224     * Returns the error message arguments.
225     *
226     * @return arguments for formatting this error message. You need to use
227     *         <code>MessageFormat</code> class to format the message. It can
228     *         be null.
229     *
230     * @supported.api
231     */
232    public Object[] getMessageArgs() {
233        return _args;
234    }
235
236    /**
237     * Gets messages of the exceptions including the nested exceptions.
238     *
239     * @return messages of the exceptions including nested exceptions. The
240     *         returned string is formed by concatenating messages of all the
241     *         exceptions, with a new line separator, starting from this
242     *         exception, all the way to the root exception, by following the
243     *         nested exceptions. The message returned is always in English
244     *         locale. To get localized message, use the getL10NMessage(Locale)
245     *         method.
246     *
247     * @supported.api
248     */
249    public String getMessage() {
250        return getL10NMessage(Locale.ENGLISH);
251    }
252}