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 */ 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 } 454