Rework XMPP Error class design

Introduce AbstractError, change 'Conditions' to enums. Because of
AbstractError, it was necessary that PlainStreamElement and
TopLevelStreamElement becomes an interface. Thus the implementation of
TopLevelStreamElement.toString() had to be removed.

This adds

- policy-violation
- unexpected-request

to XMPPError.Condition, and removes the

- payment-required
- remote-server-error
- unexpected-condition
- request-timeout

Conditions

The file transfer code does now no longer throw XMPPErrorExceptions, but
SmackExceptions.

Fixes SMACK-608. Makes it possible to resolves SMACK-386.
This commit is contained in:
Florian Schmaus 2014-11-25 13:11:24 +01:00
parent cc09192095
commit 9286a1decb
31 changed files with 582 additions and 548 deletions

View File

@ -277,9 +277,8 @@ public class ReconnectionManager {
if (e instanceof StreamErrorException) {
StreamErrorException xmppEx = (StreamErrorException) e;
StreamError error = xmppEx.getStreamError();
String reason = error.getCode();
if ("conflict".equals(reason)) {
if (StreamError.Condition.conflict == error.getCondition()) {
return;
}
}

View File

@ -0,0 +1,111 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class AbstractError {
private final String textNamespace;
protected final Map<String, String> descriptiveTexts;
private final List<PacketExtension> extensions;
protected AbstractError(Map<String, String> descriptiveTexts) {
this(descriptiveTexts, null);
}
protected AbstractError(Map<String, String> descriptiveTexts, List<PacketExtension> extensions) {
this(descriptiveTexts, null, extensions);
}
protected AbstractError(Map<String, String> descriptiveTexts, String textNamespace, List<PacketExtension> extensions) {
if (descriptiveTexts != null) {
this.descriptiveTexts = descriptiveTexts;
} else {
this.descriptiveTexts = Collections.emptyMap();
}
this.textNamespace = textNamespace;
if (extensions != null) {
this.extensions = extensions;
} else {
this.extensions = Collections.emptyList();
}
}
/**
* Get the descriptive text of this SASLFailure.
* <p>
* Returns the descriptive text of this SASLFailure in the system default language if possible. May return null.
* </p>
*
* @return the descriptive text or null.
*/
public String getDescriptiveText() {
String defaultLocale = Locale.getDefault().getLanguage();
String descriptiveText = getDescriptiveText(defaultLocale);
if (descriptiveText == null) {
descriptiveText = getDescriptiveText("");
}
return descriptiveText;
}
/**
* Get the descriptive test of this SASLFailure.
* <p>
* Returns the descriptive text of this SASLFailure in the given language. May return null if not available.
* </p>
*
* @param xmllang the language.
* @return the descriptive text or null.
*/
public String getDescriptiveText(String xmllang) {
return descriptiveTexts.get(xmllang);
}
/**
* Returns the first packet 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 <PE extends PacketExtension> PE getExtension(String elementName, String namespace) {
return PacketUtil.packetExtensionfromCollection(extensions, elementName, namespace);
}
protected void addDescriptiveTextsAndExtensions(XmlStringBuilder xml) {
for (Map.Entry<String, String> entry : descriptiveTexts.entrySet()) {
String xmllang = entry.getKey();
String text = entry.getValue();
xml.halfOpenElement("text").xmlnsAttribute(textNamespace)
.xmllangAttribute(xmllang).rightAngleBracket();
xml.escape(text);
xml.closeElement("text");
}
for (PacketExtension packetExtension : extensions) {
xml.append(packetExtension.toXML());
}
}
}

View File

@ -23,6 +23,6 @@ package org.jivesoftware.smack.packet;
*
* @author Florian Schmaus
*/
public abstract class FullStreamElement extends PlainStreamElement implements PacketExtension {
public abstract class FullStreamElement implements PlainStreamElement, PacketExtension {
}

View File

@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong;
*
* @author Matt Tucker
*/
public abstract class Packet extends TopLevelStreamElement {
public abstract class Packet implements TopLevelStreamElement {
public static final String TEXT = "text";
public static final String ITEM = "item";

View File

@ -29,6 +29,6 @@ package org.jivesoftware.smack.packet;
*
* @author Florian Schmaus
*/
public abstract class PlainStreamElement extends TopLevelStreamElement {
public interface PlainStreamElement extends TopLevelStreamElement {
}

View File

@ -17,6 +17,11 @@
package org.jivesoftware.smack.packet;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
* Represents a stream error packet. Stream errors are unrecoverable errors where the server
* will close the unrelying TCP connection after the stream error was sent to the client.
@ -73,48 +78,114 @@ package org.jivesoftware.smack.packet;
* <tr><td> not-well-formed </td><td> the initiating entity has sent XML that is not
* well-formed. </td></tr>
* </table>
* <p>
* Stream error syntax:
* <pre>
* {@code
* <stream:error>
* <defined-condition xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
* [<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'
* xml:lang='langcode'>
* OPTIONAL descriptive text
* </text>]
* [OPTIONAL application-specific condition element]
* </stream:error>
* }
* </pre>
*
* @author Gaston Dombiak
*/
public class StreamError {
public class StreamError extends AbstractError implements PlainStreamElement {
public static final String ELEMENT = "stream:error";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams";
private String code;
private String text;
private final Condition condition;
private final String conditionText;
public StreamError(String code) {
super();
this.code = code;
public StreamError(Condition condition, String conditionText, Map<String, String> descriptiveTexts, List<PacketExtension> extensions) {
super(descriptiveTexts, extensions);
if (conditionText != null) {
switch (condition) {
case see_other_host:
break;
default:
throw new IllegalArgumentException("The given condition '" + condition
+ "' can not contain a conditionText");
}
}
this.condition = condition;
this.conditionText = conditionText;
}
public StreamError(String code, String text) {
this(code);
this.text = text;
public Condition getCondition() {
return condition;
}
/**
* Returns the error code.
*
* @return the error code.
*/
public String getCode() {
return code;
}
/**
* Returns the error text, which may be null.
*
* @return the error text.
*/
public String getText() {
return text;
public String getConditionText() {
return conditionText;
}
@Override
public String toString() {
StringBuilder txt = new StringBuilder();
txt.append("stream:error (").append(code).append(")");
if (text != null) txt.append(" text: ").append(text);
return txt.toString();
return toXML().toString();
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.openElement(ELEMENT);
xml.rightAngleBracket().append(condition.toString()).xmlnsAttribute(NAMESPACE).closeEmptyElement();
addDescriptiveTextsAndExtensions(xml);
xml.closeElement(ELEMENT);
return xml;
}
/**
* The defined stream error condtions, see RFC 6120 § 4.9.3
*
*/
public enum Condition {
bad_format,
bad_namespace_prefix,
conflict,
connection_timeout,
host_gone,
host_unkown,
improper_addressing,
internal_server_error,
invalid_from,
invalid_namespace,
invalid_xml,
not_authorized,
not_well_formed,
policy_violation,
remote_connection_failed,
reset,
resource_constraint,
restricted_xml,
see_other_host,
system_shutdown,
undeficed_condition,
unsupported_encoding,
unsupported_feature,
unsupported_stanza_type,
unsupported_version;
@Override
public String toString() {
return this.name().replace('_', '-');
}
public static Condition fromString(String string) {
string = string.replace('-', '_');
Condition condition = null;
try {
condition = Condition.valueOf(string);
} catch (Exception e) {
throw new IllegalStateException("Could not transform string '" + string + "' to XMPPErrorConditoin", e);
}
return condition;
}
}
}

View File

@ -21,11 +21,6 @@ package org.jivesoftware.smack.packet;
* A XMPP top level stream element. This is either a stanza ({@link Packet}) or
* just a plain stream element ({@link PlainStreamElement}).
*/
public abstract class TopLevelStreamElement implements Element {
@Override
public final String toString() {
return toXML().toString();
}
public interface TopLevelStreamElement extends Element {
}

View File

@ -16,14 +16,12 @@
*/
package org.jivesoftware.smack.packet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
@ -32,85 +30,76 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* error condition as well as as an optional text explanation. Typical errors are:<p>
*
* <table border=1>
* <hr><td><b>XMPP Error</b></td><td><b>Type</b></td></hr>
* <tr><td>internal-server-error</td><td>WAIT</td></tr>
* <tr><td>forbidden</td><td>AUTH</td></tr>
* <tr><td>bad-request</td><td>MODIFY</td></tr>
* <tr><td>item-not-found</td><td>CANCEL</td></tr>
* <tr><td>conflict</td><td>CANCEL</td></tr>
* <tr><td>feature-not-implemented</td><td>CANCEL</td></tr>
* <tr><td>gone</td><td>MODIFY</td></tr>
* <tr><td>jid-malformed</td><td>MODIFY</td></tr>
* <tr><td>not-acceptable</td><td> MODIFY</td></tr>
* <tr><td>not-allowed</td><td>CANCEL</td></tr>
* <tr><td>not-authorized</td><td>AUTH</td></tr>
* <tr><td>payment-required</td><td>AUTH</td></tr>
* <tr><td>recipient-unavailable</td><td>WAIT</td></tr>
* <tr><td>redirect</td><td>MODIFY</td></tr>
* <tr><td>registration-required</td><td>AUTH</td></tr>
* <tr><td>remote-server-not-found</td><td>CANCEL</td></tr>
* <tr><td>remote-server-timeout</td><td>WAIT</td></tr>
* <tr><td>remote-server-error</td><td>CANCEL</td></tr>
* <tr><td>resource-constraint</td><td>WAIT</td></tr>
* <tr><td>service-unavailable</td><td>CANCEL</td></tr>
* <tr><td>subscription-required</td><td>AUTH</td></tr>
* <tr><td>undefined-condition</td><td>WAIT</td></tr>
* <tr><td>unexpected-condition</td><td>WAIT</td></tr>
* <tr><td>request-timeout</td><td>CANCEL</td></tr>
* <hr><td><b>XMPP Error Condition</b></td><td><b>Type</b></td><td><b>RFC 6120 Section</b></td></hr>
* <tr><td>bad-request</td><td>MODIFY</td><td>8.3.3.1</td></tr>
* <tr><td>conflict</td><td>CANCEL</td><td>8.3.3.2</td></tr>
* <tr><td>feature-not-implemented</td><td>CANCEL</td><td>8.3.3.3</td></tr>
* <tr><td>forbidden</td><td>AUTH</td><td>8.3.3.4</td></tr>
* <tr><td>gone</td><td>MODIFY</td><td>8.3.3.5</td></tr>
* <tr><td>internal-server-error</td><td>WAIT</td><td>8.3.3.6</td></tr>
* <tr><td>item-not-found</td><td>CANCEL</td><td>8.3.3.7</td></tr>
* <tr><td>jid-malformed</td><td>MODIFY</td><td>8.3.3.8</td></tr>
* <tr><td>not-acceptable</td><td> MODIFY</td><td>8.3.3.9</td></tr>
* <tr><td>not-allowed</td><td>CANCEL</td><td>8.3.3.10</td></tr>
* <tr><td>not-authorized</td><td>AUTH</td><td>8.3.3.11</td></tr>
* <tr><td>policy-violation</td><td>AUTH</td><td>8.3.3.12</td></tr>
* <tr><td>recipient-unavailable</td><td>WAIT</td><td>8.3.3.13</td></tr>
* <tr><td>redirect</td><td>MODIFY</td><td>8.3.3.14</td></tr>
* <tr><td>registration-required</td><td>AUTH</td><td>8.3.3.15</td></tr>
* <tr><td>remote-server-not-found</td><td>CANCEL</td><td>8.3.3.16</td></tr>
* <tr><td>remote-server-timeout</td><td>WAIT</td><td>8.3.3.17</td></tr>
* <tr><td>resource-constraint</td><td>WAIT</td><td>8.3.3.18</td></tr>
* <tr><td>service-unavailable</td><td>CANCEL</td><td>8.3.3.19</td></tr>
* <tr><td>subscription-required</td><td>AUTH</td><td>8.3.3.20</td></tr>
* <tr><td>undefined-condition</td><td>WAIT</td><td>8.3.3.21</td></tr>
* <tr><td>unexpected-request</td><td>WAIT</td><td>8.3.3.22</td></tr>
* </table>
*
* @author Matt Tucker
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#stanzas-error-syntax">RFC 6120 - 8.3.2 Syntax: The Syntax of XMPP error stanzas</a>
*/
public class XMPPError {
public class XMPPError extends AbstractError {
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas";
public static final String ERROR = "error";
private static final Logger LOGGER = Logger.getLogger(XMPPError.class.getName());
private static final Map<Condition, Type> CONDITION_TO_TYPE = new HashMap<Condition, Type>();
static {
CONDITION_TO_TYPE.put(Condition.bad_request, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.conflict, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.feature_not_implemented, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.forbidden, Type.AUTH);
CONDITION_TO_TYPE.put(Condition.gone, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.internal_server_error, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.item_not_found, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.jid_malformed, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.not_acceptable, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.not_allowed, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.not_authorized, Type.AUTH);
CONDITION_TO_TYPE.put(Condition.policy_violation, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.recipient_unavailable, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.redirect, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.registration_required, Type.AUTH);
CONDITION_TO_TYPE.put(Condition.remote_server_not_found, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.remote_server_timeout, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.resource_constraint, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY);
}
private final Condition condition;
private final String conditionText;
private final String errorGenerator;
private final Type type;
private final String condition;
private String message;
private List<PacketExtension> applicationExtensions = null;
public XMPPError(String condition) {
this(new Condition(condition));
}
/**
* Creates a new error with the specified condition inferring the type.
* 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) {
// Look for the condition and its default 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 inferred type.
type = defaultErrorSpecification.getType();
} else {
type = null;
}
this(condition, null, null, null, null, null);
}
/**
* Creates a new error with the specified condition and message infering the type.
* 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(condition);
this.message = messageText;
public XMPPError(Condition condition, PacketExtension applicationSpecificCondition) {
this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition));
}
/**
@ -121,15 +110,36 @@ public class XMPPError {
*
* @param type the error type.
* @param condition the error condition.
* @param message a message describing the error.
* @param extension list of packet extensions
* @param descriptiveTexts
* @param extensions list of packet extensions
*/
public XMPPError(Type type, String condition, String message,
List<PacketExtension> extension) {
this.type = type;
public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
List<PacketExtension> extensions) {
super(descriptiveTexts, NAMESPACE, extensions);
this.condition = condition;
this.message = message;
this.applicationExtensions = extension;
if (conditionText != null) {
switch (condition) {
case gone:
case redirect:
break;
default:
throw new IllegalArgumentException(
"Condition text can only be set with condtion types 'gone' and 'redirect', not "
+ condition);
}
}
this.conditionText = conditionText;
this.errorGenerator = errorGenerator;
if (type == null) {
Type determinedType = CONDITION_TO_TYPE.get(condition);
if (determinedType == null) {
LOGGER.warning("Could not determine type for condition: " + condition);
determinedType = Type.CANCEL;
}
this.type = determinedType;
} else {
this.type = type;
}
}
/**
@ -137,7 +147,7 @@ public class XMPPError {
*
* @return the error condition.
*/
public String getCondition() {
public Condition getCondition() {
return condition;
}
@ -150,13 +160,12 @@ public class XMPPError {
return type;
}
/**
* 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;
public String getErrorGenerator() {
return errorGenerator;
}
public String getConditionText() {
return conditionText;
}
/**
@ -167,93 +176,24 @@ public class XMPPError {
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ERROR);
xml.optAttribute("type", type.name().toLowerCase(Locale.US));
xml.attribute("type", type.toString());
xml.optAttribute("by", errorGenerator);
xml.rightAngleBracket();
if (condition != null) {
xml.halfOpenElement(condition);
xml.xmlnsAttribute(NAMESPACE);
xml.closeEmptyElement();
}
if (message != null) {
xml.halfOpenElement(Packet.TEXT);
xml.xmllangAttribute("en");
xml.xmlnsAttribute(NAMESPACE);
xml.rightAngleBracket();
xml.escape(message);
xml.closeElement(Packet.TEXT);
}
for (PacketExtension element : this.getExtensions()) {
xml.append(element.toXML());
}
xml.halfOpenElement(condition.toString());
xml.xmlnsAttribute(NAMESPACE);
xml.closeEmptyElement();
addDescriptiveTextsAndExtensions(xml);
xml.closeElement(ERROR);
return xml;
}
public String toString() {
StringBuilder txt = new StringBuilder();
if (condition != null) {
txt.append(condition);
}
if (message != null) {
txt.append(" ").append(message);
}
return txt.toString();
}
/**
* Returns a List of 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 a List of the error extensions.
*/
public synchronized List<PacketExtension> getExtensions() {
if (applicationExtensions == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(applicationExtensions);
}
/**
* Returns the first packet 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;
public static XMPPError from(Condition condition, String descriptiveText) {
Map<String, String> descriptiveTexts = new HashMap<String, String>();
descriptiveTexts.put("en", descriptiveText);
return new XMPPError(condition, null, null, null, descriptiveTexts, null);
}
/**
@ -272,161 +212,60 @@ public class XMPPError {
CANCEL,
MODIFY,
AUTH,
CONTINUE
}
CONTINUE;
/**
* A class to represent predefined error conditions.
*/
public static class Condition implements CharSequence {
public static final Condition internal_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 not_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 final String value;
public Condition(String value) {
this.value = value;
}
@Override
@Override
public String toString() {
return value;
// Locale.US not required, since Type consists only of ASCII chars
return name().toLowerCase();
}
public static Type fromString(String string) {
// Locale.US not required, since Type consists only of ASCII chars
string = string.toUpperCase();
return Type.valueOf(string);
}
}
public enum Condition {
bad_request,
conflict,
feature_not_implemented,
forbidden,
gone,
internal_server_error,
item_not_found,
jid_malformed,
not_acceptable,
not_allowed,
not_authorized,
policy_violation,
recipient_unavailable,
redirect,
registration_required,
remote_server_not_found,
remote_server_timeout,
resource_constraint,
service_unavailable,
subscription_required,
undefined_condition,
unexpected_request;
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
public String toString() {
return this.name().replace('_', '-');
}
public static Condition fromString(String string) {
string = string.replace('-', '_');
Condition condition = null;
try {
condition = Condition.valueOf(string);
} catch (Exception e) {
throw new IllegalStateException("Could not transform string '" + string + "' to XMPPErrorConditoin", e);
}
return toString().equals(other.toString());
}
public boolean equals(CharSequence other) {
return StringUtils.nullSafeCharSequenceEquals(this, other);
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public int length() {
return value.length();
}
@Override
public char charAt(int index) {
return value.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return value.subSequence(start, end);
return condition;
}
}
/**
* A class to represent the error specification used to infer common usage.
*/
private static class ErrorSpecification {
private static Map<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>();
private final Type type;
@SuppressWarnings("unused")
private final Condition condition;
private ErrorSpecification(Condition condition, Type type) {
this.type = type;
this.condition = condition;
}
static {
instances.put(Condition.internal_server_error, new ErrorSpecification(
Condition.internal_server_error, Type.WAIT));
instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden,
Type.AUTH));
instances.put(Condition.bad_request, new XMPPError.ErrorSpecification(
Condition.bad_request, Type.MODIFY));
instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification(
Condition.item_not_found, Type.CANCEL));
instances.put(Condition.conflict, new XMPPError.ErrorSpecification(
Condition.conflict, Type.CANCEL));
instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification(
Condition.feature_not_implemented, Type.CANCEL));
instances.put(Condition.gone, new XMPPError.ErrorSpecification(
Condition.gone, Type.MODIFY));
instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification(
Condition.jid_malformed, Type.MODIFY));
instances.put(Condition.not_acceptable, new XMPPError.ErrorSpecification(
Condition.not_acceptable, Type.MODIFY));
instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification(
Condition.not_allowed, Type.CANCEL));
instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification(
Condition.not_authorized, Type.AUTH));
instances.put(Condition.payment_required, new XMPPError.ErrorSpecification(
Condition.payment_required, Type.AUTH));
instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification(
Condition.recipient_unavailable, Type.WAIT));
instances.put(Condition.redirect, new XMPPError.ErrorSpecification(
Condition.redirect, Type.MODIFY));
instances.put(Condition.registration_required, new XMPPError.ErrorSpecification(
Condition.registration_required, Type.AUTH));
instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification(
Condition.remote_server_not_found, Type.CANCEL));
instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification(
Condition.remote_server_timeout, Type.WAIT));
instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification(
Condition.remote_server_error, Type.CANCEL));
instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification(
Condition.resource_constraint, Type.WAIT));
instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification(
Condition.service_unavailable, Type.CANCEL));
instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification(
Condition.subscription_required, Type.AUTH));
instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification(
Condition.undefined_condition, Type.WAIT));
instances.put(Condition.unexpected_request, new XMPPError.ErrorSpecification(
Condition.unexpected_request, Type.WAIT));
instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification(
Condition.request_timeout, Type.CANCEL));
}
protected static ErrorSpecification specFor(Condition condition) {
return instances.get(condition);
}
/**
* Returns the error type.
*
* @return the error type.
*/
protected Type getType() {
return type;
}
}
}

View File

@ -16,10 +16,9 @@
*/
package org.jivesoftware.smack.sasl.packet;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import org.jivesoftware.smack.packet.AbstractError;
import org.jivesoftware.smack.packet.PlainStreamElement;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.util.StringUtils;
@ -31,7 +30,7 @@ public class SaslStreamElements {
/**
* Initiating SASL authentication by select a mechanism.
*/
public static class AuthMechanism extends PlainStreamElement {
public static class AuthMechanism implements PlainStreamElement {
public static final String ELEMENT = "auth";
private final String mechanism;
@ -69,7 +68,7 @@ public class SaslStreamElements {
/**
* A SASL challenge stream element.
*/
public static class Challenge extends PlainStreamElement {
public static class Challenge implements PlainStreamElement {
public static final String ELEMENT = "challenge";
private final String data;
@ -91,7 +90,7 @@ public class SaslStreamElements {
/**
* A SASL response stream element.
*/
public static class Response extends PlainStreamElement {
public static class Response implements PlainStreamElement {
public static final String ELEMENT = "response";
private final String authenticationText;
@ -121,7 +120,7 @@ public class SaslStreamElements {
/**
* A SASL success stream element.
*/
public static class Success extends PlainStreamElement {
public static class Success implements PlainStreamElement {
public static final String ELEMENT = "success";
final private String data;
@ -156,25 +155,21 @@ public class SaslStreamElements {
}
/**
* A SASL failure stream element.
* A SASL failure stream element, also called "SASL Error"
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#sasl-errors">RFC 6120 6.5 SASL Errors</a>
*/
public static class SASLFailure extends PlainStreamElement {
public static class SASLFailure extends AbstractError implements PlainStreamElement {
public static final String ELEMENT = "failure";
private final SASLError saslError;
private final String saslErrorString;
private final Map<String, String> descriptiveTexts;
public SASLFailure(String saslError) {
this(saslError, null);
}
public SASLFailure(String saslError, Map<String, String> descriptiveTexts) {
if (descriptiveTexts != null) {
this.descriptiveTexts = descriptiveTexts;
} else {
this.descriptiveTexts = Collections.emptyMap();
}
super(descriptiveTexts);
SASLError error = SASLError.fromString(saslError);
if (error == null) {
// RFC6120 6.5 states that unknown condition must be treat as generic authentication
@ -203,50 +198,19 @@ public class SaslStreamElements {
return saslErrorString;
}
/**
* Get the descriptive text of this SASLFailure.
* <p>
* Returns the descriptive text of this SASLFailure in the system default language if possible. May return null.
* </p>
*
* @return the descriptive text or null.
*/
public String getDescriptiveText() {
String defaultLocale = Locale.getDefault().getLanguage();
String descriptiveText = getDescriptiveText(defaultLocale);
if (descriptiveText == null) {
descriptiveText = getDescriptiveText(null);
}
return descriptiveText;
}
/**
* Get the descriptive test of this SASLFailure.
* <p>
* Returns the descriptive text of this SASLFailure in the given language. May return null if not available.
* </p>
*
* @param xmllang the language.
* @return the descriptive text or null.
*/
public String getDescriptiveText(String xmllang) {
return descriptiveTexts.get(xmllang);
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
xml.emptyElement(saslErrorString);
for (Map.Entry<String, String> entry : descriptiveTexts.entrySet()) {
String xmllang = entry.getKey();
String text = entry.getValue();
xml.halfOpenElement("text").xmllangAttribute(xmllang).rightAngleBracket();
xml.escape(text);
xml.closeElement("text");
}
addDescriptiveTextsAndExtensions(xml);
xml.closeElement(ELEMENT);
return xml;
}
@Override
public String toString() {
return toXML().toString();
}
}
}

View File

@ -25,7 +25,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -762,6 +761,18 @@ public class PacketParserUtils {
return new Compress.Feature(methods);
}
public static Map<String, String> parseDescriptiveTexts(XmlPullParser parser, Map<String, String> descriptiveTexts)
throws XmlPullParserException, IOException {
if (descriptiveTexts == null) {
descriptiveTexts = new HashMap<String, String>();
}
String xmllang = getLanguageAttribute(parser);
String text = parser.nextText();
String previousValue = descriptiveTexts.put(xmllang, text);
assert (previousValue == null);
return descriptiveTexts;
}
/**
* Parses SASL authentication error packets.
*
@ -773,17 +784,14 @@ public class PacketParserUtils {
public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException {
final int initialDepth = parser.getDepth();
String condition = null;
Map<String, String> descriptiveTexts = new HashMap<String, String>();
Map<String, String> descriptiveTexts = null;
outerloop: while (true) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if (name.equals("text")) {
String xmllang = getLanguageAttribute(parser);
String text = parser.nextText();
String previousValue = descriptiveTexts.put(xmllang, text);
assert(previousValue == null);
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
}
else {
assert(condition == null);
@ -806,37 +814,51 @@ public class PacketParserUtils {
* @param parser the XML parser.
* @return an stream error packet.
* @throws XmlPullParserException if an exception occurs while parsing the packet.
* @throws SmackException
*/
public static StreamError parseStreamError(XmlPullParser parser) throws IOException,
XmlPullParserException {
final int depth = parser.getDepth();
boolean done = false;
String code = null;
String text = null;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
String namespace = parser.getNamespace();
if (StreamError.NAMESPACE.equals(namespace)) {
public static StreamError parseStreamError(XmlPullParser parser) throws IOException, XmlPullParserException,
SmackException {
final int initialDepth = parser.getDepth();
List<PacketExtension> extensions = new ArrayList<PacketExtension>();
Map<String, String> descriptiveTexts = null;
StreamError.Condition condition = null;
String conditionText = null;
outerloop: while (true) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if (name.equals(Packet.TEXT) && !parser.isEmptyElementTag()) {
parser.next();
text = parser.getText();
String namespace = parser.getNamespace();
switch (namespace) {
case StreamError.NAMESPACE:
switch (name) {
case "text":
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
break;
default:
// If it's not a text element, that is qualified by the StreamError.NAMESPACE,
// then it has to be the stream error code
condition = StreamError.Condition.fromString(name);
if (!parser.isEmptyElementTag()) {
conditionText = parser.nextText();
}
break;
}
break;
default:
PacketParserUtils.addPacketExtension(extensions, parser, name, namespace);
break;
}
else {
// If it's not a text element, that is qualified by the StreamError.NAMESPACE,
// then it has to be the stream error code
code = name;
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
else if (eventType == XmlPullParser.END_TAG && depth == parser.getDepth()) {
done = true;
}
return new StreamError(condition, conditionText, descriptiveTexts, extensions);
}
return new StreamError(code, text);
}
/**
* Parses error sub-packets.
@ -849,50 +871,47 @@ public class PacketParserUtils {
*/
public static XMPPError parseError(XmlPullParser parser)
throws XmlPullParserException, IOException, SmackException {
String type = null;
String message = null;
String condition = null;
final int initialDepth = parser.getDepth();
Map<String, String> descriptiveTexts = null;
XMPPError.Condition condition = null;
String conditionText = null;
List<PacketExtension> extensions = new ArrayList<PacketExtension>();
// Parse the error header
type = parser.getAttributeValue("", "type");
// Parse the text and condition tags
outerloop:
while (true) {
XMPPError.Type errorType = XMPPError.Type.fromString(parser.getAttributeValue("", "type"));
String errorGenerator = parser.getAttributeValue("", "by");
outerloop: while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(Packet.TEXT)) {
message = parser.nextText();
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
String namespace = parser.getNamespace();
switch (namespace) {
case XMPPError.NAMESPACE:
switch (name) {
case Packet.TEXT:
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
break;
default:
condition = XMPPError.Condition.fromString(name);
if (!parser.isEmptyElementTag()) {
conditionText = parser.nextText();
}
break;
}
break;
default:
PacketParserUtils.addPacketExtension(extensions, parser, name, namespace);
}
else {
// Condition tag, it can be xmpp error or an application defined error.
String elementName = parser.getName();
String namespace = parser.getNamespace();
if (namespace.equals(XMPPError.NAMESPACE)) {
condition = elementName;
}
else {
PacketParserUtils.addPacketExtension(extensions, parser, elementName, namespace);
}
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("error")) {
break outerloop;
}
}
}
// Parse the error type.
XMPPError.Type errorType = XMPPError.Type.CANCEL;
try {
if (type != null) {
errorType = XMPPError.Type.valueOf(type.toUpperCase(Locale.US));
}
}
catch (IllegalArgumentException iae) {
LOGGER.log(Level.SEVERE, "Could not find error type for " + type.toUpperCase(Locale.US), iae);
}
return new XMPPError(errorType, condition, message, extensions);
return new XMPPError(condition, conditionText, errorGenerator, errorType, descriptiveTexts, extensions);
}
/**

View File

@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.jivesoftware.smack.packet.StreamError.Condition;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
@ -44,7 +45,7 @@ public class StreamErrorTest {
fail(e.getMessage());
}
assertNotNull(error);
assertEquals("conflict", error.getCode());
assertEquals(Condition.conflict, error.getCondition());
}
@Test
@ -68,8 +69,8 @@ public class StreamErrorTest {
fail(e.getMessage());
}
assertNotNull(error);
assertEquals("conflict", error.getCode());
assertEquals("Replaced by new connection", error.getText());
assertEquals(Condition.conflict, error.getCondition());
assertEquals("Replaced by new connection", error.getDescriptiveText());
}
@Test
@ -84,7 +85,7 @@ public class StreamErrorTest {
"<text xml:lang='' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>" +
"Replaced by new connection" +
"</text>" +
"<appSpecificElement>" +
"<appSpecificElement xmlns='myns'>" +
"Text contents of application-specific condition element: Foo Bar" +
"</appSpecificElement>" +
"</stream:error>" +
@ -96,10 +97,10 @@ public class StreamErrorTest {
fail(e.getMessage());
}
assertNotNull(error);
assertEquals("conflict", error.getCode());
assertEquals("Replaced by new connection", error.getText());
// As of now, Smack ignores application-specific condition elements, so we don't
// test them.
assertEquals(Condition.conflict, error.getCondition());
assertEquals("Replaced by new connection", error.getDescriptiveText());
PacketExtension appSpecificElement = error.getExtension("appSpecificElement", "myns");
assertNotNull(appSpecificElement);
}
}

View File

@ -280,7 +280,7 @@ public class Socks5BytestreamRequest implements BytestreamRequest {
*/
private void cancelRequest() throws XMPPErrorException, NotConnectedException {
String errorMessage = "Could not establish socket with any provided host";
XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, errorMessage);
XMPPError error = XMPPError.from(XMPPError.Condition.item_not_found, errorMessage);
IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error);
this.manager.getConnection().sendPacket(errorIQ);
throw new XMPPErrorException(errorMessage, error);

View File

@ -593,8 +593,7 @@ public class AdHocCommandManager extends Manager {
private void respondError(AdHocCommandData response, XMPPError.Condition condition,
AdHocCommand.SpecificErrorCondition specificCondition) throws NotConnectedException
{
XMPPError error = new XMPPError(condition);
error.addExtension(new AdHocCommandData.SpecificError(specificCondition));
XMPPError error = new XMPPError(condition, new AdHocCommandData.SpecificError(specificCondition));
respondError(response, error);
}

View File

@ -0,0 +1,45 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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.smackx.filetransfer;
import org.jivesoftware.smack.SmackException;
public abstract class FileTransferException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 1L;
public static class NoStreamMethodsOfferedException extends FileTransferException {
/**
*
*/
private static final long serialVersionUID = 1L;
}
public static class NoAcceptableTransferMechanisms extends FileTransferException {
/**
*
*/
private static final long serialVersionUID = 1L;
}
}

View File

@ -37,6 +37,8 @@ import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.filetransfer.FileTransferException.NoAcceptableTransferMechanisms;
import org.jivesoftware.smackx.filetransfer.FileTransferException.NoStreamMethodsOfferedException;
import org.jivesoftware.smackx.si.packet.StreamInitiation;
import org.jivesoftware.smackx.xdata.Form;
import org.jivesoftware.smackx.xdata.FormField;
@ -176,32 +178,32 @@ public class FileTransferNegotiator extends Manager {
*
* @param request The related file transfer request.
* @return The file transfer object that handles the transfer
* @throws XMPPErrorException If there are either no stream methods contained in the packet, or
* @throws NoStreamMethodsOfferedException If there are either no stream methods contained in the packet, or
* there is not an appropriate stream method.
* @throws NotConnectedException
* @throws NoAcceptableTransferMechanisms
*/
public StreamNegotiator selectStreamNegotiator(
FileTransferRequest request) throws XMPPErrorException, NotConnectedException {
FileTransferRequest request) throws NotConnectedException, NoStreamMethodsOfferedException, NoAcceptableTransferMechanisms {
StreamInitiation si = request.getStreamInitiation();
FormField streamMethodField = getStreamMethodField(si
.getFeatureNegotiationForm());
if (streamMethodField == null) {
String errorMessage = "No stream methods contained in packet.";
XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage);
String errorMessage = "No stream methods contained in stanza.";
XMPPError error = XMPPError.from(XMPPError.Condition.bad_request, errorMessage);
IQ iqPacket = IQ.createErrorResponse(si, error);
connection().sendPacket(iqPacket);
throw new XMPPErrorException(errorMessage, error);
throw new FileTransferException.NoStreamMethodsOfferedException();
}
// select the appropriate protocol
StreamNegotiator selectedStreamNegotiator;
try {
selectedStreamNegotiator = getNegotiator(streamMethodField);
}
catch (XMPPErrorException e) {
IQ iqPacket = IQ.createErrorResponse(si, e.getXMPPError());
catch (NoAcceptableTransferMechanisms e) {
IQ iqPacket = IQ.createErrorResponse(si, XMPPError.from(XMPPError.Condition.bad_request, "No acceptable transfer mechanism"));
connection().sendPacket(iqPacket);
throw e;
}
@ -221,7 +223,7 @@ public class FileTransferNegotiator extends Manager {
}
private StreamNegotiator getNegotiator(final FormField field)
throws XMPPErrorException {
throws NoAcceptableTransferMechanisms {
String variable;
boolean isByteStream = false;
boolean isIBB = false;
@ -236,9 +238,7 @@ public class FileTransferNegotiator extends Manager {
}
if (!isByteStream && !isIBB) {
XMPPError error = new XMPPError(XMPPError.Condition.bad_request,
"No acceptable transfer mechanism");
throw new XMPPErrorException(error);
throw new FileTransferException.NoAcceptableTransferMechanisms();
}
if (isByteStream && isIBB) {
@ -299,10 +299,11 @@ public class FileTransferNegotiator extends Manager {
* @throws XMPPErrorException Thrown if there is an error negotiating the file transfer.
* @throws NotConnectedException
* @throws NoResponseException
* @throws NoAcceptableTransferMechanisms
*/
public StreamNegotiator negotiateOutgoingTransfer(final String userID,
final String streamID, final String fileName, final long size,
final String desc, int responseTimeout) throws XMPPErrorException, NotConnectedException, NoResponseException {
final String desc, int responseTimeout) throws XMPPErrorException, NotConnectedException, NoResponseException, NoAcceptableTransferMechanisms {
StreamInitiation si = new StreamInitiation();
si.setSessionID(streamID);
si.setMimeType(URLConnection.guessContentTypeFromName(fileName));
@ -337,8 +338,7 @@ public class FileTransferNegotiator extends Manager {
}
}
private StreamNegotiator getOutgoingNegotiator(final FormField field)
throws XMPPErrorException {
private StreamNegotiator getOutgoingNegotiator(final FormField field) throws NoAcceptableTransferMechanisms {
boolean isByteStream = false;
boolean isIBB = false;
for (String variable : field.getValues()) {
@ -351,9 +351,7 @@ public class FileTransferNegotiator extends Manager {
}
if (!isByteStream && !isIBB) {
XMPPError error = new XMPPError(XMPPError.Condition.bad_request,
"No acceptable transfer mechanism");
throw new XMPPErrorException(error);
throw new FileTransferException.NoAcceptableTransferMechanisms();
}
if (isByteStream && isIBB) {

View File

@ -341,16 +341,15 @@ public class OutgoingFileTransfer extends FileTransfer {
private void handleXMPPException(XMPPErrorException e) {
XMPPError error = e.getXMPPError();
if (error != null) {
String condition = error.getCondition();
if (XMPPError.Condition.forbidden.equals(condition)) {
switch (error.getCondition()) {
case forbidden:
setStatus(Status.refused);
return;
}
else if (XMPPError.Condition.bad_request.equals(condition)) {
case bad_request:
setStatus(Status.error);
setError(Error.not_acceptable);
}
else {
break;
default:
setStatus(FileTransfer.Status.error);
}
}

View File

@ -73,7 +73,7 @@ public class CloseListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
argument.getValue().getError().getCondition());
}

View File

@ -75,7 +75,7 @@ public class DataListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
argument.getValue().getError().getCondition());
}

View File

@ -120,7 +120,7 @@ public class InBandBytestreamManagerTest {
fail("exception should be thrown");
}
catch (XMPPErrorException e) {
assertEquals(XMPPError.Condition.feature_not_implemented.toString(),
assertEquals(XMPPError.Condition.feature_not_implemented,
e.getXMPPError().getCondition());
}

View File

@ -83,7 +83,7 @@ public class InBandBytestreamRequestTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.not_acceptable.toString(),
assertEquals(XMPPError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
}

View File

@ -368,7 +368,7 @@ public class InBandBytestreamSessionTest {
protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() {
public void verify(IQ request, IQ response) {
assertEquals(XMPPError.Condition.unexpected_request.toString(),
assertEquals(XMPPError.Condition.unexpected_request,
request.getError().getCondition());
}
@ -406,7 +406,7 @@ public class InBandBytestreamSessionTest {
protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() {
public void verify(IQ request, IQ response) {
assertEquals(XMPPError.Condition.bad_request.toString(),
assertEquals(XMPPError.Condition.bad_request,
request.getError().getCondition());
}

View File

@ -91,7 +91,7 @@ public class InitiationListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.not_acceptable.toString(),
assertEquals(XMPPError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
}
@ -119,7 +119,7 @@ public class InitiationListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.resource_constraint.toString(),
assertEquals(XMPPError.Condition.resource_constraint,
argument.getValue().getError().getCondition());
}
@ -209,7 +209,7 @@ public class InitiationListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.not_acceptable.toString(),
assertEquals(XMPPError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
}

View File

@ -98,7 +98,7 @@ public class InitiationListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.not_acceptable.toString(),
assertEquals(XMPPError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
}
@ -188,7 +188,7 @@ public class InitiationListenerTest {
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(XMPPError.Condition.not_acceptable.toString(),
assertEquals(XMPPError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
}

View File

@ -113,7 +113,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition());
}
@ -157,7 +157,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition());
}
@ -205,7 +205,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition());
}
@ -297,7 +297,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(),
assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition());
}

View File

@ -33,7 +33,6 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleMediaListener;
@ -1030,9 +1029,8 @@ public class JingleSession extends JingleNegotiator implements MediaReceivedList
public IQ createJingleError(IQ iq, JingleError jingleError) {
IQ errorPacket = null;
if (jingleError != null) {
List<PacketExtension> extList = new ArrayList<PacketExtension>();
extList.add(jingleError);
XMPPError error = new XMPPError(XMPPError.Type.CANCEL, jingleError.toString(), "", extList);
// TODO This is wrong according to XEP-166 § 10, but this jingle implementation is deprecated anyways
XMPPError error = new XMPPError(XMPPError.Condition.undefined_condition, jingleError);
errorPacket = IQ.createErrorResponse(iq, error);

View File

@ -113,7 +113,7 @@ public class MediaNegotiator extends JingleNegotiator {
setNegotiatorState(JingleNegotiatorState.FAILED);
triggerMediaClosed(getBestCommonAudioPt());
// This next line seems wrong, and may subvert the normal closing process.
throw new JingleException(iq.getError().getMessage());
throw new JingleException(iq.getError().getDescriptiveText());
} else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {

View File

@ -603,7 +603,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
setNegotiatorState(JingleNegotiatorState.FAILED);
triggerTransportClosed(null);
// This next line seems wrong, and may subvert the normal closing process.
throw new JingleException(iq.getError().getMessage());
throw new JingleException(iq.getError().getDescriptiveText());
} else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {

View File

@ -1101,7 +1101,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
break;
case Failed.ELEMENT:
Failed failed = ParseStreamManagement.failed(parser);
XMPPError xmppError = failed.getXMPPError();
XMPPError xmppError = new XMPPError(failed.getXMPPErrorCondition());
XMPPException xmppException = new XMPPErrorException("Stream Management failed", xmppError);
// If only XEP-198 would specify different failure elements for the SM
// enable and SM resume failure case. But this is not the case, so we

View File

@ -189,25 +189,26 @@ public class StreamManagement {
public static class Failed extends FullStreamElement {
public static final String ELEMENT = "failed";
private XMPPError error;
private XMPPError.Condition condition;
public Failed() {
}
public Failed(XMPPError error) {
this.error = error;
public Failed(XMPPError.Condition condition) {
this.condition = condition;
}
public XMPPError getXMPPError() {
return error;
public XMPPError.Condition getXMPPErrorCondition() {
return condition;
}
@Override
public CharSequence toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
if (error != null) {
if (condition != null) {
xml.rightAngleBracket();
xml.append(error.toXML());
xml.append(condition.toString());
xml.xmlnsAttribute(XMPPError.NAMESPACE);
xml.closeElement(ELEMENT);
}
else {

View File

@ -44,7 +44,7 @@ public class ParseStreamManagement {
public static Failed failed(XmlPullParser parser) throws XmlPullParserException, IOException {
ParserUtils.assertAtStartTag(parser);
String name;
String condition = "unknown";
XMPPError.Condition condition = null;
outerloop:
while(true) {
int event = parser.next();
@ -53,7 +53,7 @@ public class ParseStreamManagement {
name = parser.getName();
String namespace = parser.getNamespace();
if (XMPPError.NAMESPACE.equals(namespace)) {
condition = name;
condition = XMPPError.Condition.fromString(name);
}
break;
case XmlPullParser.END_TAG:
@ -65,8 +65,7 @@ public class ParseStreamManagement {
}
}
ParserUtils.assertAtEndTag(parser);
XMPPError error = new XMPPError(condition);
return new Failed(error);
return new Failed(condition);
}
public static Resumed resumed(XmlPullParser parser) throws XmlPullParserException, IOException {

View File

@ -19,6 +19,7 @@
package org.jivesoftware.smack.tcp.sm.provider;
import com.jamesmurty.utils.XMLBuilder;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.tcp.sm.packet.StreamManagement;
import org.jivesoftware.smack.util.PacketParserUtils;
@ -34,6 +35,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class ParseStreamManagementTest {
private static final Properties outputProperties = initOutputProperties();
@ -83,19 +85,16 @@ public class ParseStreamManagementTest {
PacketParserUtils.getParserFor(failedStanza));
assertThat(failedPacket, is(notNullValue()));
XMPPError error = failedPacket.getXMPPError();
assertThat(error, is(notNullValue()));
assertThat(error.getCondition(), equalTo("unknown"));
assertTrue(failedPacket.getXMPPErrorCondition() == null);
}
@Test
public void testParseFailedError() throws Exception {
String errorCondition = "failure";
XMPPError.Condition errorCondition = XMPPError.Condition.unexpected_request;
String failedStanza = XMLBuilder.create("failed")
.a("xmlns", "urn:xmpp:sm:3")
.element(errorCondition, XMPPError.NAMESPACE)
.element(errorCondition.toString(), XMPPError.NAMESPACE)
.asString(outputProperties);
System.err.println(failedStanza);
@ -104,10 +103,7 @@ public class ParseStreamManagementTest {
PacketParserUtils.getParserFor(failedStanza));
assertThat(failedPacket, is(notNullValue()));
XMPPError error = failedPacket.getXMPPError();
assertThat(error, is(notNullValue()));
assertThat(error.getCondition(), equalTo(errorCondition));
assertTrue(failedPacket.getXMPPErrorCondition() == errorCondition);
}
@Test