aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/packet/XMPPError.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smack/packet/XMPPError.java')
-rw-r--r--src/org/jivesoftware/smack/packet/XMPPError.java453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/packet/XMPPError.java b/src/org/jivesoftware/smack/packet/XMPPError.java
new file mode 100644
index 0000000..770a09c
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/XMPPError.java
@@ -0,0 +1,453 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jivesoftware.smack.packet;
+
+import java.util.*;
+
+/**
+ * Represents a XMPP error sub-packet. Typically, a server responds to a request that has
+ * problems by sending the packet back and including an error packet. Each error has a code, type,
+ * error condition as well as as an optional text explanation. Typical errors are:<p>
+ *
+ * <table border=1>
+ * <hr><td><b>Code</b></td><td><b>XMPP Error</b></td><td><b>Type</b></td></hr>
+ * <tr><td>500</td><td>interna-server-error</td><td>WAIT</td></tr>
+ * <tr><td>403</td><td>forbidden</td><td>AUTH</td></tr>
+ * <tr><td>400</td<td>bad-request</td><td>MODIFY</td>></tr>
+ * <tr><td>404</td><td>item-not-found</td><td>CANCEL</td></tr>
+ * <tr><td>409</td><td>conflict</td><td>CANCEL</td></tr>
+ * <tr><td>501</td><td>feature-not-implemented</td><td>CANCEL</td></tr>
+ * <tr><td>302</td><td>gone</td><td>MODIFY</td></tr>
+ * <tr><td>400</td><td>jid-malformed</td><td>MODIFY</td></tr>
+ * <tr><td>406</td><td>no-acceptable</td><td> MODIFY</td></tr>
+ * <tr><td>405</td><td>not-allowed</td><td>CANCEL</td></tr>
+ * <tr><td>401</td><td>not-authorized</td><td>AUTH</td></tr>
+ * <tr><td>402</td><td>payment-required</td><td>AUTH</td></tr>
+ * <tr><td>404</td><td>recipient-unavailable</td><td>WAIT</td></tr>
+ * <tr><td>302</td><td>redirect</td><td>MODIFY</td></tr>
+ * <tr><td>407</td><td>registration-required</td><td>AUTH</td></tr>
+ * <tr><td>404</td><td>remote-server-not-found</td><td>CANCEL</td></tr>
+ * <tr><td>504</td><td>remote-server-timeout</td><td>WAIT</td></tr>
+ * <tr><td>502</td><td>remote-server-error</td><td>CANCEL</td></tr>
+ * <tr><td>500</td><td>resource-constraint</td><td>WAIT</td></tr>
+ * <tr><td>503</td><td>service-unavailable</td><td>CANCEL</td></tr>
+ * <tr><td>407</td><td>subscription-required</td><td>AUTH</td></tr>
+ * <tr><td>500</td><td>undefined-condition</td><td>WAIT</td></tr>
+ * <tr><td>400</td><td>unexpected-condition</td><td>WAIT</td></tr>
+ * <tr><td>408</td><td>request-timeout</td><td>CANCEL</td></tr>
+ * </table>
+ *
+ * @author Matt Tucker
+ */
+public class XMPPError {
+
+ private int code;
+ private Type type;
+ private String condition;
+ private String message;
+ private List<PacketExtension> applicationExtensions = null;
+
+
+ /**
+ * Creates a new error with the specified condition infering the type and code.
+ * If the Condition is predefined, client code should be like:
+ * new XMPPError(XMPPError.Condition.remote_server_timeout);
+ * If the Condition is not predefined, invocations should be like
+ * new XMPPError(new XMPPError.Condition("my_own_error"));
+ *
+ * @param condition the error condition.
+ */
+ public XMPPError(Condition condition) {
+ this.init(condition);
+ this.message = null;
+ }
+
+ /**
+ * Creates a new error with the specified condition and message infering the type and code.
+ * If the Condition is predefined, client code should be like:
+ * new XMPPError(XMPPError.Condition.remote_server_timeout, "Error Explanation");
+ * If the Condition is not predefined, invocations should be like
+ * new XMPPError(new XMPPError.Condition("my_own_error"), "Error Explanation");
+ *
+ * @param condition the error condition.
+ * @param messageText a message describing the error.
+ */
+ public XMPPError(Condition condition, String messageText) {
+ this.init(condition);
+ this.message = messageText;
+ }
+
+ /**
+ * Creates a new error with the specified code and no message.
+ *
+ * @param code the error code.
+ * @deprecated new errors should be created using the constructor XMPPError(condition)
+ */
+ public XMPPError(int code) {
+ this.code = code;
+ this.message = null;
+ }
+
+ /**
+ * Creates a new error with the specified code and message.
+ * deprecated
+ *
+ * @param code the error code.
+ * @param message a message describing the error.
+ * @deprecated new errors should be created using the constructor XMPPError(condition, message)
+ */
+ public XMPPError(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ /**
+ * Creates a new error with the specified code, type, condition and message.
+ * This constructor is used when the condition is not recognized automatically by XMPPError
+ * i.e. there is not a defined instance of ErrorCondition or it does not applies the default
+ * specification.
+ *
+ * @param code the error code.
+ * @param type the error type.
+ * @param condition the error condition.
+ * @param message a message describing the error.
+ * @param extension list of packet extensions
+ */
+ public XMPPError(int code, Type type, String condition, String message,
+ List<PacketExtension> extension) {
+ this.code = code;
+ this.type = type;
+ this.condition = condition;
+ this.message = message;
+ this.applicationExtensions = extension;
+ }
+
+ /**
+ * Initialize the error infering the type and code for the received condition.
+ *
+ * @param condition the error condition.
+ */
+ private void init(Condition condition) {
+ // Look for the condition and its default code and type
+ ErrorSpecification defaultErrorSpecification = ErrorSpecification.specFor(condition);
+ this.condition = condition.value;
+ if (defaultErrorSpecification != null) {
+ // If there is a default error specification for the received condition,
+ // it get configured with the infered type and code.
+ this.type = defaultErrorSpecification.getType();
+ this.code = defaultErrorSpecification.getCode();
+ }
+ }
+ /**
+ * Returns the error condition.
+ *
+ * @return the error condition.
+ */
+ public String getCondition() {
+ return condition;
+ }
+
+ /**
+ * Returns the error type.
+ *
+ * @return the error type.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return the error code.
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Returns the message describing the error, or null if there is no message.
+ *
+ * @return the message describing the error, or null if there is no message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the error as XML.
+ *
+ * @return the error as XML.
+ */
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<error code=\"").append(code).append("\"");
+ if (type != null) {
+ buf.append(" type=\"");
+ buf.append(type.name());
+ buf.append("\"");
+ }
+ buf.append(">");
+ if (condition != null) {
+ buf.append("<").append(condition);
+ buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>");
+ }
+ if (message != null) {
+ buf.append("<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">");
+ buf.append(message);
+ buf.append("</text>");
+ }
+ for (PacketExtension element : this.getExtensions()) {
+ buf.append(element.toXML());
+ }
+ buf.append("</error>");
+ return buf.toString();
+ }
+
+ public String toString() {
+ StringBuilder txt = new StringBuilder();
+ if (condition != null) {
+ txt.append(condition);
+ }
+ txt.append("(").append(code).append(")");
+ if (message != null) {
+ txt.append(" ").append(message);
+ }
+ return txt.toString();
+ }
+
+ /**
+ * Returns an Iterator for the error extensions attached to the xmppError.
+ * An application MAY provide application-specific error information by including a
+ * properly-namespaced child in the error element.
+ *
+ * @return an Iterator for the error extensions.
+ */
+ public synchronized List<PacketExtension> getExtensions() {
+ if (applicationExtensions == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(applicationExtensions);
+ }
+
+ /**
+ * Returns the first patcket extension that matches the specified element name and
+ * namespace, or <tt>null</tt> if it doesn't exist.
+ *
+ * @param elementName the XML element name of the packet extension.
+ * @param namespace the XML element namespace of the packet extension.
+ * @return the extension, or <tt>null</tt> if it doesn't exist.
+ */
+ public synchronized PacketExtension getExtension(String elementName, String namespace) {
+ if (applicationExtensions == null || elementName == null || namespace == null) {
+ return null;
+ }
+ for (PacketExtension ext : applicationExtensions) {
+ if (elementName.equals(ext.getElementName()) && namespace.equals(ext.getNamespace())) {
+ return ext;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a packet extension to the error.
+ *
+ * @param extension a packet extension.
+ */
+ public synchronized void addExtension(PacketExtension extension) {
+ if (applicationExtensions == null) {
+ applicationExtensions = new ArrayList<PacketExtension>();
+ }
+ applicationExtensions.add(extension);
+ }
+
+ /**
+ * Set the packet extension to the error.
+ *
+ * @param extension a packet extension.
+ */
+ public synchronized void setExtension(List<PacketExtension> extension) {
+ applicationExtensions = extension;
+ }
+
+ /**
+ * A class to represent the type of the Error. The types are:
+ *
+ * <ul>
+ * <li>XMPPError.Type.WAIT - retry after waiting (the error is temporary)
+ * <li>XMPPError.Type.CANCEL - do not retry (the error is unrecoverable)
+ * <li>XMPPError.Type.MODIFY - retry after changing the data sent
+ * <li>XMPPError.Type.AUTH - retry after providing credentials
+ * <li>XMPPError.Type.CONTINUE - proceed (the condition was only a warning)
+ * </ul>
+ */
+ public static enum Type {
+ WAIT,
+ CANCEL,
+ MODIFY,
+ AUTH,
+ CONTINUE
+ }
+
+ /**
+ * A class to represent predefined error conditions.
+ */
+ public static class Condition {
+
+ public static final Condition interna_server_error = new Condition("internal-server-error");
+ public static final Condition forbidden = new Condition("forbidden");
+ public static final Condition bad_request = new Condition("bad-request");
+ public static final Condition conflict = new Condition("conflict");
+ public static final Condition feature_not_implemented = new Condition("feature-not-implemented");
+ public static final Condition gone = new Condition("gone");
+ public static final Condition item_not_found = new Condition("item-not-found");
+ public static final Condition jid_malformed = new Condition("jid-malformed");
+ public static final Condition no_acceptable = new Condition("not-acceptable");
+ public static final Condition not_allowed = new Condition("not-allowed");
+ public static final Condition not_authorized = new Condition("not-authorized");
+ public static final Condition payment_required = new Condition("payment-required");
+ public static final Condition recipient_unavailable = new Condition("recipient-unavailable");
+ public static final Condition redirect = new Condition("redirect");
+ public static final Condition registration_required = new Condition("registration-required");
+ public static final Condition remote_server_error = new Condition("remote-server-error");
+ public static final Condition remote_server_not_found = new Condition("remote-server-not-found");
+ public static final Condition remote_server_timeout = new Condition("remote-server-timeout");
+ public static final Condition resource_constraint = new Condition("resource-constraint");
+ public static final Condition service_unavailable = new Condition("service-unavailable");
+ public static final Condition subscription_required = new Condition("subscription-required");
+ public static final Condition undefined_condition = new Condition("undefined-condition");
+ public static final Condition unexpected_request = new Condition("unexpected-request");
+ public static final Condition request_timeout = new Condition("request-timeout");
+
+ private String value;
+
+ public Condition(String value) {
+ this.value = value;
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+
+
+ /**
+ * A class to represent the error specification used to infer common usage.
+ */
+ private static class ErrorSpecification {
+ private int code;
+ private Type type;
+ private Condition condition;
+ private static Map<Condition, ErrorSpecification> instances = errorSpecifications();
+
+ private ErrorSpecification(Condition condition, Type type, int code) {
+ this.code = code;
+ this.type = type;
+ this.condition = condition;
+ }
+
+ private static Map<Condition, ErrorSpecification> errorSpecifications() {
+ Map<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>(22);
+ instances.put(Condition.interna_server_error, new ErrorSpecification(
+ Condition.interna_server_error, Type.WAIT, 500));
+ instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden,
+ Type.AUTH, 403));
+ instances.put(Condition.bad_request, new XMPPError.ErrorSpecification(
+ Condition.bad_request, Type.MODIFY, 400));
+ instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification(
+ Condition.item_not_found, Type.CANCEL, 404));
+ instances.put(Condition.conflict, new XMPPError.ErrorSpecification(
+ Condition.conflict, Type.CANCEL, 409));
+ instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification(
+ Condition.feature_not_implemented, Type.CANCEL, 501));
+ instances.put(Condition.gone, new XMPPError.ErrorSpecification(
+ Condition.gone, Type.MODIFY, 302));
+ instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification(
+ Condition.jid_malformed, Type.MODIFY, 400));
+ instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification(
+ Condition.no_acceptable, Type.MODIFY, 406));
+ instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification(
+ Condition.not_allowed, Type.CANCEL, 405));
+ instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification(
+ Condition.not_authorized, Type.AUTH, 401));
+ instances.put(Condition.payment_required, new XMPPError.ErrorSpecification(
+ Condition.payment_required, Type.AUTH, 402));
+ instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification(
+ Condition.recipient_unavailable, Type.WAIT, 404));
+ instances.put(Condition.redirect, new XMPPError.ErrorSpecification(
+ Condition.redirect, Type.MODIFY, 302));
+ instances.put(Condition.registration_required, new XMPPError.ErrorSpecification(
+ Condition.registration_required, Type.AUTH, 407));
+ instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification(
+ Condition.remote_server_not_found, Type.CANCEL, 404));
+ instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification(
+ Condition.remote_server_timeout, Type.WAIT, 504));
+ instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification(
+ Condition.remote_server_error, Type.CANCEL, 502));
+ instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification(
+ Condition.resource_constraint, Type.WAIT, 500));
+ instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification(
+ Condition.service_unavailable, Type.CANCEL, 503));
+ instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification(
+ Condition.subscription_required, Type.AUTH, 407));
+ instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification(
+ Condition.undefined_condition, Type.WAIT, 500));
+ instances.put(Condition.unexpected_request, new XMPPError.ErrorSpecification(
+ Condition.unexpected_request, Type.WAIT, 400));
+ instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification(
+ Condition.request_timeout, Type.CANCEL, 408));
+
+ return instances;
+ }
+
+ protected static ErrorSpecification specFor(Condition condition) {
+ return instances.get(condition);
+ }
+
+ /**
+ * Returns the error condition.
+ *
+ * @return the error condition.
+ */
+ protected Condition getCondition() {
+ return condition;
+ }
+
+ /**
+ * Returns the error type.
+ *
+ * @return the error type.
+ */
+ protected Type getType() {
+ return type;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return the error code.
+ */
+ protected int getCode() {
+ return code;
+ }
+ }
+}