blob: 770a09ca21b3962cb9046dd03c5a07d4b94659a4 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
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
21package org.jivesoftware.smack.packet;
22
23import 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 */
60public 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 */
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 */
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 */
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 */
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 */
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 */
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 */
164 public String getCondition() {
165 return condition;
166 }
167
168 /**
169 * Returns the error type.
170 *
171 * @return the error type.
172 */
173 public Type getType() {
174 return type;
175 }
176
177 /**
178 * Returns the error code.
179 *
180 * @return the error code.
181 */
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 */
191 public String getMessage() {
192 return message;
193 }
194
195 /**
196 * Returns the error as XML.
197 *
198 * @return the error as XML.
199 */
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
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 */
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 */
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 */
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 */
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
343 public Condition(String value) {
344 this.value = value;
345 }
346
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
362 private ErrorSpecification(Condition condition, Type type, int code) {
363 this.code = code;
364 this.type = type;
365 this.condition = condition;
366 }
367
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
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 */
431 protected Condition getCondition() {
432 return condition;
433 }
434
435 /**
436 * Returns the error type.
437 *
438 * @return the error type.
439 */
440 protected Type getType() {
441 return type;
442 }
443
444 /**
445 * Returns the error code.
446 *
447 * @return the error code.
448 */
449 protected int getCode() {
450 return code;
451 }
452 }
453}