001/**
002 *
003 * Copyright 2003-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.jivesoftware.smack;
019
020import org.jivesoftware.smack.packet.Nonza;
021import org.jivesoftware.smack.packet.Stanza;
022import org.jivesoftware.smack.packet.StanzaError;
023import org.jivesoftware.smack.packet.StreamError;
024
025import org.jxmpp.jid.Jid;
026
027/**
028 * A generic exception that is thrown when an error occurs performing an
029 * XMPP operation. XMPP servers can respond to error conditions with an error code
030 * and textual description of the problem, which are encapsulated in the XMPPError
031 * class. When appropriate, an XMPPError instance is attached instances of this exception.<p>
032 *
033 * When a stream error occurred, the server will send a stream error to the client before
034 * closing the connection. Stream errors are unrecoverable errors. When a stream error
035 * is sent to the client an XMPPException will be thrown containing the StreamError sent
036 * by the server.
037 *
038 * @see StanzaError
039 * @author Matt Tucker
040 */
041public abstract class XMPPException extends Exception {
042    private static final long serialVersionUID = 6881651633890968625L;
043
044
045    /**
046     * Creates a new XMPPException.
047     */
048    protected XMPPException() {
049        super();
050    }
051
052    /**
053     * Creates a new XMPPException with a description of the exception.
054     *
055     * @param message description of the exception.
056     */
057    protected XMPPException(String message) {
058        super(message);
059    }
060
061    /**
062     * Creates a new XMPPException with a description of the exception and the
063     * Throwable that was the root cause of the exception.
064     *
065     * @param message a description of the exception.
066     * @param wrappedThrowable the root cause of the exception.
067     */
068    protected XMPPException(String message, Throwable wrappedThrowable) {
069        super(message, wrappedThrowable);
070    }
071
072    /**
073     * An exception caused by an XMPP error stanza response on the protocol level. You can examine the underlying
074     * {@link StanzaError} by calling {@link #getStanzaError()}.
075     */
076    public static class XMPPErrorException extends XMPPException {
077        /**
078         *
079         */
080        private static final long serialVersionUID = 212790389529249604L;
081        private final StanzaError error;
082        private final Stanza stanza;
083
084        /**
085         * The request which resulted in the XMPP protocol error response. May be {@code null}.
086         */
087        private final Stanza request;
088
089        /**
090         * Creates a new XMPPErrorException with the given builder.
091         *
092         * @param xmppErrorBuilder
093         * @deprecated Use {@link #XMPPErrorException(Stanza, StanzaError)} instead.
094         */
095        @Deprecated
096        public XMPPErrorException(StanzaError.Builder xmppErrorBuilder) {
097            this(null, xmppErrorBuilder.build());
098        }
099
100        /**
101         * Creates a new XMPPErrorException with the XMPPError that was the root case of the exception.
102         *
103         * @param stanza stanza that contained the exception.
104         * @param error the root cause of the exception.
105         */
106        public XMPPErrorException(Stanza stanza, StanzaError error) {
107            this(stanza, error, null);
108        }
109
110        /**
111         * Creates a new XMPPErrorException with the XMPPError that was the root case of the exception.
112         *
113         * @param request the request which triggered the error.
114         * @param stanza stanza that contained the exception.
115         * @param error the root cause of the exception.
116         * @since 4.3.0
117         */
118        public XMPPErrorException(Stanza stanza, StanzaError error, Stanza request) {
119            super();
120            this.error = error;
121            this.stanza = stanza;
122            this.request = request;
123        }
124
125        /**
126         * Returns the XMPPError associated with this exception, or <tt>null</tt> if there isn't
127         * one.
128         *
129         * @return the XMPPError associated with this exception.
130         * @deprecated use {@link #getStanzaError()} instead.
131         */
132        @Deprecated
133        // TODO Remove in Smack 4.4.
134        public StanzaError getXMPPError() {
135            return error;
136        }
137
138        /**
139         * Returns the stanza error extension element associated with this exception.
140         *
141         * @return the stanza error extension element associated with this exception.
142         */
143        public StanzaError getStanzaError() {
144            return error;
145        }
146
147        /**
148         * Get the request which triggered the error response causing this exception.
149         *
150         * @return the request or {@code null}.
151         * @since 4.3.0
152         */
153        public Stanza getRequest() {
154            return request;
155        }
156
157        @Override
158        public String getMessage() {
159            StringBuilder sb = new StringBuilder();
160
161            if (stanza != null) {
162                Jid from = stanza.getFrom();
163                if (from != null) {
164                    sb.append("XMPP error reply received from " + from + ": ");
165                }
166            }
167
168            sb.append(error);
169
170            if (request != null) {
171                sb.append(" as result of the following request: ");
172                sb.append(request);
173            }
174
175            return sb.toString();
176        }
177
178        public static void ifHasErrorThenThrow(Stanza packet) throws XMPPErrorException {
179            ifHasErrorThenThrow(packet, null);
180        }
181
182        public static void ifHasErrorThenThrow(Stanza packet, Stanza request) throws XMPPErrorException {
183            StanzaError xmppError = packet.getError();
184            if (xmppError != null) {
185                throw new XMPPErrorException(packet, xmppError, request);
186            }
187        }
188    }
189
190    public static class FailedNonzaException extends XMPPException {
191
192        /**
193         *
194         */
195        private static final long serialVersionUID = 1L;
196
197        private final StanzaError.Condition condition;
198
199        private final Nonza nonza;
200
201        public FailedNonzaException(Nonza nonza, StanzaError.Condition condition) {
202            this.condition = condition;
203            this.nonza = nonza;
204        }
205
206        public StanzaError.Condition getCondition() {
207            return condition;
208        }
209
210        public Nonza getNonza() {
211            return nonza;
212        }
213    }
214
215    public static class StreamErrorException extends XMPPException {
216        /**
217         *
218         */
219        private static final long serialVersionUID = 3400556867134848886L;
220        private final StreamError streamError;
221
222        /**
223         * Creates a new XMPPException with the stream error that was the root case of the
224         * exception. When a stream error is received from the server then the underlying connection
225         * will be closed by the server.
226         *
227         * @param streamError the root cause of the exception.
228         */
229        public StreamErrorException(StreamError streamError) {
230            super(streamError.getCondition().toString()
231                  + " You can read more about the meaning of this stream error at http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions\n"
232                  + streamError.toString());
233            this.streamError = streamError;
234        }
235
236        /**
237         * Returns the StreamError associated with this exception. The underlying TCP connection is
238         * closed by the server after sending the stream error to the client.
239         *
240         * @return the StreamError associated with this exception.
241         */
242        public StreamError getStreamError() {
243            return streamError;
244        }
245
246    }
247}