001/**
002 *
003 * Copyright 2014-2015 Florian Schmaus
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 */
017package org.jivesoftware.smack;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.jivesoftware.smack.filter.StanzaFilter;
023import org.jivesoftware.smack.util.dns.HostAddress;
024import org.jxmpp.jid.Jid;
025
026/**
027 * Smack uses SmackExceptions for errors that are not defined by any XMPP specification.
028 * 
029 * @author Florian Schmaus
030 */
031public class SmackException extends Exception {
032
033    /**
034     * 
035     */
036    private static final long serialVersionUID = 1844674365368214457L;
037
038    /**
039     * Creates a new SmackException with the Throwable that was the root cause of the exception.
040     * 
041     * @param wrappedThrowable the root cause of the exception.
042     */
043    public SmackException(Throwable wrappedThrowable) {
044        super(wrappedThrowable);
045    }
046
047    public SmackException(String message) {
048        super(message);
049    }
050
051    public SmackException(String message, Throwable wrappedThrowable) {
052        super(message, wrappedThrowable);
053    }
054
055    protected SmackException() {
056    }
057
058    /**
059     * Exception thrown always when there was no response to an request within the stanza(/packet) reply timeout of the used
060     * connection instance. You can modify (e.g. increase) the stanza(/packet) reply timeout with
061     * {@link XMPPConnection#setReplyTimeout(long)}.
062     */
063    public static final class NoResponseException extends SmackException {
064        /**
065         * 
066         */
067        private static final long serialVersionUID = -6523363748984543636L;
068
069        private final StanzaFilter filter;
070
071        private NoResponseException(String message) {
072            this(message, null);
073        }
074
075        private NoResponseException(String message, StanzaFilter filter) {
076            super(message);
077            this.filter = filter;
078        }
079
080        /**
081         * Get the filter that was used to collect the response.
082         *
083         * @return the used filter or <code>null</code>.
084         */
085        public StanzaFilter getFilter() {
086            return filter;
087        }
088
089        public static NoResponseException newWith(XMPPConnection connection, String waitingFor) {
090            final StringBuilder sb = getWaitingFor(connection);
091            sb.append(" While waiting for ").append(waitingFor);
092            return new NoResponseException(sb.toString());
093        }
094
095        public static NoResponseException newWith(XMPPConnection connection,
096                        StanzaCollector collector) {
097            return newWith(connection, collector.getStanzaFilter());
098        }
099
100        public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
101            final StringBuilder sb = getWaitingFor(connection);
102            sb.append(" Waited for response using: ");
103            if (filter != null) {
104                sb.append(filter.toString());
105            }
106            else {
107                sb.append("No filter used or filter was 'null'");
108            }
109            sb.append('.');
110            return new NoResponseException(sb.toString(), filter);
111        }
112
113        private static StringBuilder getWaitingFor(XMPPConnection connection) {
114            final long replyTimeout = connection.getReplyTimeout();
115            final StringBuilder sb = new StringBuilder(256);
116            sb.append("No response received within reply timeout. Timeout was "
117                            + replyTimeout + "ms (~"
118                            + replyTimeout / 1000 + "s).");
119            return sb;
120        }
121    }
122
123    public static class NotLoggedInException extends SmackException {
124
125        /**
126         * 
127         */
128        private static final long serialVersionUID = 3216216839100019278L;
129
130        public NotLoggedInException() {
131            super("Client is not logged in");
132        }
133    }
134
135    public static class AlreadyLoggedInException extends SmackException {
136
137        /**
138         * 
139         */
140        private static final long serialVersionUID = 5011416918049935231L;
141
142        public AlreadyLoggedInException() {
143            super("Client is already logged in");
144        }
145    }
146
147    public static class AlreadyConnectedException extends SmackException {
148
149        /**
150         * 
151         */
152        private static final long serialVersionUID = 5011416918049135231L;
153
154        public AlreadyConnectedException() {
155            super("Client is already connected");
156        }
157    }
158
159    public static class NotConnectedException extends SmackException {
160
161        /**
162         * 
163         */
164        private static final long serialVersionUID = 9197980400776001173L;
165
166        public NotConnectedException() {
167            this(null);
168        }
169
170        public NotConnectedException(String optionalHint) {
171            super("Client is not, or no longer, connected."
172                            + (optionalHint != null ? ' ' + optionalHint : ""));
173        }
174
175        public NotConnectedException(XMPPConnection connection, String details) {
176            super("The connection " + connection.toString() + " is no longer connected. "
177                            + details);
178        }
179
180        public NotConnectedException(XMPPConnection connection, StanzaFilter stanzaFilter) {
181            super("The connection " + connection
182                            + " is no longer connected while waiting for response with " + stanzaFilter);
183        }
184    }
185
186    public static class IllegalStateChangeException extends SmackException {
187
188        /**
189         * 
190         */
191        private static final long serialVersionUID = -1766023961577168927L;
192
193        public IllegalStateChangeException() {
194        }
195    }
196
197    public static abstract class SecurityRequiredException extends SmackException {
198
199        /**
200         * 
201         */
202        private static final long serialVersionUID = 384291845029773545L;
203
204        public SecurityRequiredException(String message) {
205            super(message);
206        }
207    }
208
209    public static class SecurityRequiredByClientException extends SecurityRequiredException {
210        /**
211         * 
212         */
213        private static final long serialVersionUID = 2395325821201543159L;
214
215        public SecurityRequiredByClientException() {
216            super("SSL/TLS required by client but not supported by server");
217        }
218    }
219
220    public static class SecurityRequiredByServerException extends SecurityRequiredException {
221        /**
222         * 
223         */
224        private static final long serialVersionUID = 8268148813117631819L;
225
226        public SecurityRequiredByServerException() {
227            super("SSL/TLS required by server but disabled in client");
228        }
229    }
230
231    public static class SecurityNotPossibleException extends SmackException {
232
233        /**
234         * 
235         */
236        private static final long serialVersionUID = -6836090872690331336L;
237
238        public SecurityNotPossibleException(String message) {
239            super(message);
240        }
241    }
242
243    /**
244     * ConnectionException is thrown if Smack is unable to connect to all hosts of a given XMPP
245     * service. The failed hosts can be retrieved with
246     * {@link ConnectionException#getFailedAddresses()}, which will have the exception causing the
247     * connection failure set and retrievable with {@link HostAddress#getExceptions()}.
248     */
249    public static class ConnectionException extends SmackException {
250
251        /**
252         * 
253         */
254        private static final long serialVersionUID = 1686944201672697996L;
255
256        private final List<HostAddress> failedAddresses;
257
258        public ConnectionException(Throwable wrappedThrowable) {
259            super(wrappedThrowable);
260            failedAddresses = new ArrayList<HostAddress>(0);
261        }
262
263        private ConnectionException(String message, List<HostAddress> failedAddresses) {
264            super(message);
265            this.failedAddresses = failedAddresses;
266        }
267
268        public static ConnectionException from(List<HostAddress> failedAddresses) {
269            final String DELIMITER = ", ";
270            StringBuilder sb = new StringBuilder("The following addresses failed: ");
271            for (HostAddress hostAddress : failedAddresses) {
272                sb.append(hostAddress.getErrorMessage());
273                sb.append(DELIMITER);
274            }
275            // Remove the last delimiter
276            sb.setLength(sb.length() - DELIMITER.length());
277            return new ConnectionException(sb.toString(), failedAddresses);
278        }
279
280        public List<HostAddress> getFailedAddresses() {
281            return failedAddresses;
282        }
283    }
284
285    public static class FeatureNotSupportedException extends SmackException {
286
287        /**
288         * 
289         */
290        private static final long serialVersionUID = 4713404802621452016L;
291
292        private final String feature;
293        private final Jid jid;
294
295        public FeatureNotSupportedException(String feature) {
296            this(feature, null);
297        }
298
299        public FeatureNotSupportedException(String feature, Jid jid) {
300            super(feature + " not supported" + (jid == null ? "" : " by '" + jid + "'"));
301            this.jid = jid;
302            this.feature = feature;
303        }
304
305        /**
306         * Get the feature which is not supported.
307         *
308         * @return the feature which is not supported
309         */
310        public String getFeature() {
311            return feature;
312        }
313
314        /**
315         * Get JID which does not support the feature. The JID can be null in cases when there are
316         * multiple JIDs queried for this feature.
317         *
318         * @return the JID which does not support the feature, or null
319         */
320        public Jid getJid() {
321            return jid;
322        }
323    }
324
325    public static class ResourceBindingNotOfferedException extends SmackException {
326
327        /**
328         * 
329         */
330        private static final long serialVersionUID = 2346934138253437571L;
331
332        public ResourceBindingNotOfferedException() {
333            super("Resource binding was not offered by server");
334        }
335    }
336}