• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision$
4  * $Date$
5  *
6  * Copyright 2003-2007 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package org.jivesoftware.smack.packet;
22 
23 import java.util.*;
24 
25 /**
26  * Represents a XMPP error sub-packet. Typically, a server responds to a request that has
27  * problems by sending the packet back and including an error packet. Each error has a code, type,
28  * error condition as well as as an optional text explanation. Typical errors are:<p>
29  *
30  * <table border=1>
31  *      <hr><td><b>Code</b></td><td><b>XMPP Error</b></td><td><b>Type</b></td></hr>
32  *      <tr><td>500</td><td>interna-server-error</td><td>WAIT</td></tr>
33  *      <tr><td>403</td><td>forbidden</td><td>AUTH</td></tr>
34  *      <tr><td>400</td<td>bad-request</td><td>MODIFY</td>></tr>
35  *      <tr><td>404</td><td>item-not-found</td><td>CANCEL</td></tr>
36  *      <tr><td>409</td><td>conflict</td><td>CANCEL</td></tr>
37  *      <tr><td>501</td><td>feature-not-implemented</td><td>CANCEL</td></tr>
38  *      <tr><td>302</td><td>gone</td><td>MODIFY</td></tr>
39  *      <tr><td>400</td><td>jid-malformed</td><td>MODIFY</td></tr>
40  *      <tr><td>406</td><td>no-acceptable</td><td> MODIFY</td></tr>
41  *      <tr><td>405</td><td>not-allowed</td><td>CANCEL</td></tr>
42  *      <tr><td>401</td><td>not-authorized</td><td>AUTH</td></tr>
43  *      <tr><td>402</td><td>payment-required</td><td>AUTH</td></tr>
44  *      <tr><td>404</td><td>recipient-unavailable</td><td>WAIT</td></tr>
45  *      <tr><td>302</td><td>redirect</td><td>MODIFY</td></tr>
46  *      <tr><td>407</td><td>registration-required</td><td>AUTH</td></tr>
47  *      <tr><td>404</td><td>remote-server-not-found</td><td>CANCEL</td></tr>
48  *      <tr><td>504</td><td>remote-server-timeout</td><td>WAIT</td></tr>
49  *      <tr><td>502</td><td>remote-server-error</td><td>CANCEL</td></tr>
50  *      <tr><td>500</td><td>resource-constraint</td><td>WAIT</td></tr>
51  *      <tr><td>503</td><td>service-unavailable</td><td>CANCEL</td></tr>
52  *      <tr><td>407</td><td>subscription-required</td><td>AUTH</td></tr>
53  *      <tr><td>500</td><td>undefined-condition</td><td>WAIT</td></tr>
54  *      <tr><td>400</td><td>unexpected-condition</td><td>WAIT</td></tr>
55  *      <tr><td>408</td><td>request-timeout</td><td>CANCEL</td></tr>
56  * </table>
57  *
58  * @author Matt Tucker
59  */
60 public class XMPPError {
61 
62     private int code;
63     private Type type;
64     private String condition;
65     private String message;
66     private List<PacketExtension> applicationExtensions = null;
67 
68 
69     /**
70      * Creates a new error with the specified condition infering the type and code.
71      * If the Condition is predefined, client code should be like:
72      *     new XMPPError(XMPPError.Condition.remote_server_timeout);
73      * If the Condition is not predefined, invocations should be like
74      *     new XMPPError(new XMPPError.Condition("my_own_error"));
75      *
76      * @param condition the error condition.
77      */
XMPPError(Condition condition)78     public XMPPError(Condition condition) {
79         this.init(condition);
80         this.message = null;
81     }
82 
83     /**
84      * Creates a new error with the specified condition and message infering the type and code.
85      * If the Condition is predefined, client code should be like:
86      *     new XMPPError(XMPPError.Condition.remote_server_timeout, "Error Explanation");
87      * If the Condition is not predefined, invocations should be like
88      *     new XMPPError(new XMPPError.Condition("my_own_error"), "Error Explanation");
89      *
90      * @param condition the error condition.
91      * @param messageText a message describing the error.
92      */
XMPPError(Condition condition, String messageText)93     public XMPPError(Condition condition, String messageText) {
94         this.init(condition);
95         this.message = messageText;
96     }
97 
98     /**
99      * Creates a new  error with the specified code and no message.
100      *
101      * @param code the error code.
102      * @deprecated new errors should be created using the constructor XMPPError(condition)
103      */
XMPPError(int code)104     public XMPPError(int code) {
105         this.code = code;
106         this.message = null;
107     }
108 
109     /**
110      * Creates a new error with the specified code and message.
111      * deprecated
112      *
113      * @param code the error code.
114      * @param message a message describing the error.
115      * @deprecated new errors should be created using the constructor XMPPError(condition, message)
116      */
XMPPError(int code, String message)117     public XMPPError(int code, String message) {
118         this.code = code;
119         this.message = message;
120     }
121 
122     /**
123      * Creates a new error with the specified code, type, condition and message.
124      * This constructor is used when the condition is not recognized automatically by XMPPError
125      * i.e. there is not a defined instance of ErrorCondition or it does not applies the default
126      * specification.
127      *
128      * @param code the error code.
129      * @param type the error type.
130      * @param condition the error condition.
131      * @param message a message describing the error.
132      * @param extension list of packet extensions
133      */
XMPPError(int code, Type type, String condition, String message, List<PacketExtension> extension)134     public XMPPError(int code, Type type, String condition, String message,
135             List<PacketExtension> extension) {
136         this.code = code;
137         this.type = type;
138         this.condition = condition;
139         this.message = message;
140         this.applicationExtensions = extension;
141     }
142 
143     /**
144      * Initialize the error infering the type and code for the received condition.
145      *
146      * @param condition the error condition.
147      */
init(Condition condition)148     private void init(Condition condition) {
149         // Look for the condition and its default code and type
150         ErrorSpecification defaultErrorSpecification = ErrorSpecification.specFor(condition);
151         this.condition = condition.value;
152         if (defaultErrorSpecification != null) {
153             // If there is a default error specification for the received condition,
154             // it get configured with the infered type and code.
155             this.type = defaultErrorSpecification.getType();
156             this.code = defaultErrorSpecification.getCode();
157         }
158     }
159     /**
160      * Returns the error condition.
161      *
162      * @return the error condition.
163      */
getCondition()164     public String getCondition() {
165         return condition;
166     }
167 
168     /**
169      * Returns the error type.
170      *
171      * @return the error type.
172      */
getType()173     public Type getType() {
174         return type;
175     }
176 
177     /**
178      * Returns the error code.
179      *
180      * @return the error code.
181      */
getCode()182     public int getCode() {
183         return code;
184     }
185 
186     /**
187      * Returns the message describing the error, or null if there is no message.
188      *
189      * @return the message describing the error, or null if there is no message.
190      */
getMessage()191     public String getMessage() {
192         return message;
193     }
194 
195     /**
196      * Returns the error as XML.
197      *
198      * @return the error as XML.
199      */
toXML()200     public String toXML() {
201         StringBuilder buf = new StringBuilder();
202         buf.append("<error code=\"").append(code).append("\"");
203         if (type != null) {
204             buf.append(" type=\"");
205             buf.append(type.name());
206             buf.append("\"");
207         }
208         buf.append(">");
209         if (condition != null) {
210             buf.append("<").append(condition);
211             buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>");
212         }
213         if (message != null) {
214             buf.append("<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">");
215             buf.append(message);
216             buf.append("</text>");
217         }
218         for (PacketExtension element : this.getExtensions()) {
219             buf.append(element.toXML());
220         }
221         buf.append("</error>");
222         return buf.toString();
223     }
224 
toString()225     public String toString() {
226         StringBuilder txt = new StringBuilder();
227         if (condition != null) {
228             txt.append(condition);
229         }
230         txt.append("(").append(code).append(")");
231         if (message != null) {
232             txt.append(" ").append(message);
233         }
234         return txt.toString();
235     }
236 
237     /**
238      * Returns an Iterator for the error extensions attached to the xmppError.
239      * An application MAY provide application-specific error information by including a
240      * properly-namespaced child in the error element.
241      *
242      * @return an Iterator for the error extensions.
243      */
getExtensions()244     public synchronized List<PacketExtension> getExtensions() {
245         if (applicationExtensions == null) {
246             return Collections.emptyList();
247         }
248         return Collections.unmodifiableList(applicationExtensions);
249     }
250 
251     /**
252      * Returns the first patcket extension that matches the specified element name and
253      * namespace, or <tt>null</tt> if it doesn't exist.
254      *
255      * @param elementName the XML element name of the packet extension.
256      * @param namespace the XML element namespace of the packet extension.
257      * @return the extension, or <tt>null</tt> if it doesn't exist.
258      */
getExtension(String elementName, String namespace)259     public synchronized PacketExtension getExtension(String elementName, String namespace) {
260         if (applicationExtensions == null || elementName == null || namespace == null) {
261             return null;
262         }
263         for (PacketExtension ext : applicationExtensions) {
264             if (elementName.equals(ext.getElementName()) && namespace.equals(ext.getNamespace())) {
265                 return ext;
266             }
267         }
268         return null;
269     }
270 
271     /**
272      * Adds a packet extension to the error.
273      *
274      * @param extension a packet extension.
275      */
addExtension(PacketExtension extension)276     public synchronized void addExtension(PacketExtension extension) {
277         if (applicationExtensions == null) {
278             applicationExtensions = new ArrayList<PacketExtension>();
279         }
280         applicationExtensions.add(extension);
281     }
282 
283     /**
284      * Set the packet extension to the error.
285      *
286      * @param extension a packet extension.
287      */
setExtension(List<PacketExtension> extension)288     public synchronized void setExtension(List<PacketExtension> extension) {
289         applicationExtensions = extension;
290     }
291 
292     /**
293      * A class to represent the type of the Error. The types are:
294      *
295      * <ul>
296      *      <li>XMPPError.Type.WAIT - retry after waiting (the error is temporary)
297      *      <li>XMPPError.Type.CANCEL - do not retry (the error is unrecoverable)
298      *      <li>XMPPError.Type.MODIFY - retry after changing the data sent
299      *      <li>XMPPError.Type.AUTH - retry after providing credentials
300      *      <li>XMPPError.Type.CONTINUE - proceed (the condition was only a warning)
301      * </ul>
302      */
303     public static enum Type {
304         WAIT,
305         CANCEL,
306         MODIFY,
307         AUTH,
308         CONTINUE
309     }
310 
311     /**
312      * A class to represent predefined error conditions.
313      */
314     public static class Condition {
315 
316         public static final Condition interna_server_error = new Condition("internal-server-error");
317         public static final Condition forbidden = new Condition("forbidden");
318         public static final Condition bad_request = new Condition("bad-request");
319         public static final Condition conflict = new Condition("conflict");
320         public static final Condition feature_not_implemented = new Condition("feature-not-implemented");
321         public static final Condition gone = new Condition("gone");
322         public static final Condition item_not_found = new Condition("item-not-found");
323         public static final Condition jid_malformed = new Condition("jid-malformed");
324         public static final Condition no_acceptable = new Condition("not-acceptable");
325         public static final Condition not_allowed = new Condition("not-allowed");
326         public static final Condition not_authorized = new Condition("not-authorized");
327         public static final Condition payment_required = new Condition("payment-required");
328         public static final Condition recipient_unavailable = new Condition("recipient-unavailable");
329         public static final Condition redirect = new Condition("redirect");
330         public static final Condition registration_required = new Condition("registration-required");
331         public static final Condition remote_server_error = new Condition("remote-server-error");
332         public static final Condition remote_server_not_found = new Condition("remote-server-not-found");
333         public static final Condition remote_server_timeout = new Condition("remote-server-timeout");
334         public static final Condition resource_constraint = new Condition("resource-constraint");
335         public static final Condition service_unavailable = new Condition("service-unavailable");
336         public static final Condition subscription_required = new Condition("subscription-required");
337         public static final Condition undefined_condition = new Condition("undefined-condition");
338         public static final Condition unexpected_request = new Condition("unexpected-request");
339         public static final Condition request_timeout = new Condition("request-timeout");
340 
341         private String value;
342 
Condition(String value)343         public Condition(String value) {
344             this.value = value;
345         }
346 
toString()347         public String toString() {
348             return value;
349         }
350     }
351 
352 
353     /**
354      * A class to represent the error specification used to infer common usage.
355      */
356     private static class ErrorSpecification {
357         private int code;
358         private Type type;
359         private Condition condition;
360         private static Map<Condition, ErrorSpecification> instances = errorSpecifications();
361 
ErrorSpecification(Condition condition, Type type, int code)362         private ErrorSpecification(Condition condition, Type type, int code) {
363             this.code = code;
364             this.type = type;
365             this.condition = condition;
366         }
367 
errorSpecifications()368         private static Map<Condition, ErrorSpecification> errorSpecifications() {
369             Map<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>(22);
370             instances.put(Condition.interna_server_error, new ErrorSpecification(
371                     Condition.interna_server_error, Type.WAIT, 500));
372             instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden,
373                     Type.AUTH, 403));
374             instances.put(Condition.bad_request, new XMPPError.ErrorSpecification(
375                     Condition.bad_request, Type.MODIFY, 400));
376             instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification(
377                     Condition.item_not_found, Type.CANCEL, 404));
378             instances.put(Condition.conflict, new XMPPError.ErrorSpecification(
379                     Condition.conflict, Type.CANCEL, 409));
380             instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification(
381                     Condition.feature_not_implemented, Type.CANCEL, 501));
382             instances.put(Condition.gone, new XMPPError.ErrorSpecification(
383                     Condition.gone, Type.MODIFY, 302));
384             instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification(
385                     Condition.jid_malformed, Type.MODIFY, 400));
386             instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification(
387                     Condition.no_acceptable, Type.MODIFY, 406));
388             instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification(
389                     Condition.not_allowed, Type.CANCEL, 405));
390             instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification(
391                     Condition.not_authorized, Type.AUTH, 401));
392             instances.put(Condition.payment_required, new XMPPError.ErrorSpecification(
393                     Condition.payment_required, Type.AUTH, 402));
394             instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification(
395                     Condition.recipient_unavailable, Type.WAIT, 404));
396             instances.put(Condition.redirect, new XMPPError.ErrorSpecification(
397                     Condition.redirect, Type.MODIFY, 302));
398             instances.put(Condition.registration_required, new XMPPError.ErrorSpecification(
399                     Condition.registration_required, Type.AUTH, 407));
400             instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification(
401                     Condition.remote_server_not_found, Type.CANCEL, 404));
402             instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification(
403                     Condition.remote_server_timeout, Type.WAIT, 504));
404             instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification(
405                     Condition.remote_server_error, Type.CANCEL, 502));
406             instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification(
407                     Condition.resource_constraint, Type.WAIT, 500));
408             instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification(
409                     Condition.service_unavailable, Type.CANCEL, 503));
410             instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification(
411                     Condition.subscription_required, Type.AUTH, 407));
412             instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification(
413                     Condition.undefined_condition, Type.WAIT, 500));
414             instances.put(Condition.unexpected_request, new XMPPError.ErrorSpecification(
415                     Condition.unexpected_request, Type.WAIT, 400));
416             instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification(
417                     Condition.request_timeout, Type.CANCEL, 408));
418 
419             return instances;
420         }
421 
specFor(Condition condition)422         protected static ErrorSpecification specFor(Condition condition) {
423             return instances.get(condition);
424         }
425 
426         /**
427          * Returns the error condition.
428          *
429          * @return the error condition.
430          */
getCondition()431         protected Condition getCondition() {
432             return condition;
433         }
434 
435         /**
436          * Returns the error type.
437          *
438          * @return the error type.
439          */
getType()440         protected Type getType() {
441             return type;
442         }
443 
444         /**
445          * Returns the error code.
446          *
447          * @return the error code.
448          */
getCode()449         protected int getCode() {
450             return code;
451         }
452     }
453 }
454