Home | History | Annotate | Download | only in packet
      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