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) { if (e instanceof StreamErrorException) {
StreamErrorException xmppEx = (StreamErrorException) e; StreamErrorException xmppEx = (StreamErrorException) e;
StreamError error = xmppEx.getStreamError(); StreamError error = xmppEx.getStreamError();
String reason = error.getCode();
if ("conflict".equals(reason)) { if (StreamError.Condition.conflict == error.getCondition()) {
return; 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 * @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 * @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 TEXT = "text";
public static final String ITEM = "item"; public static final String ITEM = "item";

View File

@ -29,6 +29,6 @@ package org.jivesoftware.smack.packet;
* *
* @author Florian Schmaus * @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; 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 * 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. * 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 * <tr><td> not-well-formed </td><td> the initiating entity has sent XML that is not
* well-formed. </td></tr> * well-formed. </td></tr>
* </table> * </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 * @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"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams";
private String code; private final Condition condition;
private String text; private final String conditionText;
public StreamError(String code) { public StreamError(Condition condition, String conditionText, Map<String, String> descriptiveTexts, List<PacketExtension> extensions) {
super(); super(descriptiveTexts, extensions);
this.code = code; 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) { public Condition getCondition() {
this(code); return condition;
this.text = text;
} }
/** public String getConditionText() {
* Returns the error code. return conditionText;
*
* @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;
} }
@Override
public String toString() { public String toString() {
StringBuilder txt = new StringBuilder(); return toXML().toString();
txt.append("stream:error (").append(code).append(")"); }
if (text != null) txt.append(" text: ").append(text);
return txt.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 * A XMPP top level stream element. This is either a stanza ({@link Packet}) or
* just a plain stream element ({@link PlainStreamElement}). * just a plain stream element ({@link PlainStreamElement}).
*/ */
public abstract class TopLevelStreamElement implements Element { public interface TopLevelStreamElement extends Element {
@Override
public final String toString() {
return toXML().toString();
}
} }

View File

@ -16,14 +16,12 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import java.util.ArrayList; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; 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> * error condition as well as as an optional text explanation. Typical errors are:<p>
* *
* <table border=1> * <table border=1>
* <hr><td><b>XMPP Error</b></td><td><b>Type</b></td></hr> * <hr><td><b>XMPP Error Condition</b></td><td><b>Type</b></td><td><b>RFC 6120 Section</b></td></hr>
* <tr><td>internal-server-error</td><td>WAIT</td></tr> * <tr><td>bad-request</td><td>MODIFY</td><td>8.3.3.1</td></tr>
* <tr><td>forbidden</td><td>AUTH</td></tr> * <tr><td>conflict</td><td>CANCEL</td><td>8.3.3.2</td></tr>
* <tr><td>bad-request</td><td>MODIFY</td></tr> * <tr><td>feature-not-implemented</td><td>CANCEL</td><td>8.3.3.3</td></tr>
* <tr><td>item-not-found</td><td>CANCEL</td></tr> * <tr><td>forbidden</td><td>AUTH</td><td>8.3.3.4</td></tr>
* <tr><td>conflict</td><td>CANCEL</td></tr> * <tr><td>gone</td><td>MODIFY</td><td>8.3.3.5</td></tr>
* <tr><td>feature-not-implemented</td><td>CANCEL</td></tr> * <tr><td>internal-server-error</td><td>WAIT</td><td>8.3.3.6</td></tr>
* <tr><td>gone</td><td>MODIFY</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></tr> * <tr><td>jid-malformed</td><td>MODIFY</td><td>8.3.3.8</td></tr>
* <tr><td>not-acceptable</td><td> MODIFY</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></tr> * <tr><td>not-allowed</td><td>CANCEL</td><td>8.3.3.10</td></tr>
* <tr><td>not-authorized</td><td>AUTH</td></tr> * <tr><td>not-authorized</td><td>AUTH</td><td>8.3.3.11</td></tr>
* <tr><td>payment-required</td><td>AUTH</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></tr> * <tr><td>recipient-unavailable</td><td>WAIT</td><td>8.3.3.13</td></tr>
* <tr><td>redirect</td><td>MODIFY</td></tr> * <tr><td>redirect</td><td>MODIFY</td><td>8.3.3.14</td></tr>
* <tr><td>registration-required</td><td>AUTH</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></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></tr> * <tr><td>remote-server-timeout</td><td>WAIT</td><td>8.3.3.17</td></tr>
* <tr><td>remote-server-error</td><td>CANCEL</td></tr> * <tr><td>resource-constraint</td><td>WAIT</td><td>8.3.3.18</td></tr>
* <tr><td>resource-constraint</td><td>WAIT</td></tr> * <tr><td>service-unavailable</td><td>CANCEL</td><td>8.3.3.19</td></tr>
* <tr><td>service-unavailable</td><td>CANCEL</td></tr> * <tr><td>subscription-required</td><td>AUTH</td><td>8.3.3.20</td></tr>
* <tr><td>subscription-required</td><td>AUTH</td></tr> * <tr><td>undefined-condition</td><td>WAIT</td><td>8.3.3.21</td></tr>
* <tr><td>undefined-condition</td><td>WAIT</td></tr> * <tr><td>unexpected-request</td><td>WAIT</td><td>8.3.3.22</td></tr>
* <tr><td>unexpected-condition</td><td>WAIT</td></tr>
* <tr><td>request-timeout</td><td>CANCEL</td></tr>
* </table> * </table>
* *
* @author Matt Tucker * @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> * @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 NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas";
public static final String ERROR = "error"; 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 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) { public XMPPError(Condition condition) {
// Look for the condition and its default type this(condition, null, null, null, null, null);
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;
}
} }
/** public XMPPError(Condition condition, PacketExtension applicationSpecificCondition) {
* Creates a new error with the specified condition and message infering the type. this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition));
* 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;
} }
/** /**
@ -121,15 +110,36 @@ public class XMPPError {
* *
* @param type the error type. * @param type the error type.
* @param condition the error condition. * @param condition the error condition.
* @param message a message describing the error. * @param descriptiveTexts
* @param extension list of packet extensions * @param extensions list of packet extensions
*/ */
public XMPPError(Type type, String condition, String message, public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
List<PacketExtension> extension) { List<PacketExtension> extensions) {
this.type = type; super(descriptiveTexts, NAMESPACE, extensions);
this.condition = condition; this.condition = condition;
this.message = message; if (conditionText != null) {
this.applicationExtensions = extension; 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. * @return the error condition.
*/ */
public String getCondition() { public Condition getCondition() {
return condition; return condition;
} }
@ -150,13 +160,12 @@ public class XMPPError {
return type; return type;
} }
/** public String getErrorGenerator() {
* Returns the message describing the error, or null if there is no message. return errorGenerator;
* }
* @return the message describing the error, or null if there is no message.
*/ public String getConditionText() {
public String getMessage() { return conditionText;
return message;
} }
/** /**
@ -167,93 +176,24 @@ public class XMPPError {
public XmlStringBuilder toXML() { public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(); XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ERROR); xml.halfOpenElement(ERROR);
xml.optAttribute("type", type.name().toLowerCase(Locale.US)); xml.attribute("type", type.toString());
xml.optAttribute("by", errorGenerator);
xml.rightAngleBracket(); xml.rightAngleBracket();
if (condition != null) { xml.halfOpenElement(condition.toString());
xml.halfOpenElement(condition); xml.xmlnsAttribute(NAMESPACE);
xml.xmlnsAttribute(NAMESPACE); xml.closeEmptyElement();
xml.closeEmptyElement();
} addDescriptiveTextsAndExtensions(xml);
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.closeElement(ERROR); xml.closeElement(ERROR);
return xml; return xml;
} }
public String toString() { public static XMPPError from(Condition condition, String descriptiveText) {
StringBuilder txt = new StringBuilder(); Map<String, String> descriptiveTexts = new HashMap<String, String>();
if (condition != null) { descriptiveTexts.put("en", descriptiveText);
txt.append(condition); return new XMPPError(condition, null, null, null, descriptiveTexts, null);
}
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;
} }
/** /**
@ -272,161 +212,60 @@ public class XMPPError {
CANCEL, CANCEL,
MODIFY, MODIFY,
AUTH, AUTH,
CONTINUE CONTINUE;
}
/** @Override
* 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
public String toString() { 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 @Override
public boolean equals(Object other) { public String toString() {
if (other == null) { return this.name().replace('_', '-');
return false; }
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()); return condition;
}
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);
} }
} }
/**
* 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; package org.jivesoftware.smack.sasl.packet;
import java.util.Collections;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.jivesoftware.smack.packet.AbstractError;
import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.PlainStreamElement;
import org.jivesoftware.smack.sasl.SASLError; import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
@ -31,7 +30,7 @@ public class SaslStreamElements {
/** /**
* Initiating SASL authentication by select a mechanism. * 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"; public static final String ELEMENT = "auth";
private final String mechanism; private final String mechanism;
@ -69,7 +68,7 @@ public class SaslStreamElements {
/** /**
* A SASL challenge stream element. * A SASL challenge stream element.
*/ */
public static class Challenge extends PlainStreamElement { public static class Challenge implements PlainStreamElement {
public static final String ELEMENT = "challenge"; public static final String ELEMENT = "challenge";
private final String data; private final String data;
@ -91,7 +90,7 @@ public class SaslStreamElements {
/** /**
* A SASL response stream element. * A SASL response stream element.
*/ */
public static class Response extends PlainStreamElement { public static class Response implements PlainStreamElement {
public static final String ELEMENT = "response"; public static final String ELEMENT = "response";
private final String authenticationText; private final String authenticationText;
@ -121,7 +120,7 @@ public class SaslStreamElements {
/** /**
* A SASL success stream element. * A SASL success stream element.
*/ */
public static class Success extends PlainStreamElement { public static class Success implements PlainStreamElement {
public static final String ELEMENT = "success"; public static final String ELEMENT = "success";
final private String data; 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"; public static final String ELEMENT = "failure";
private final SASLError saslError; private final SASLError saslError;
private final String saslErrorString; private final String saslErrorString;
private final Map<String, String> descriptiveTexts;
public SASLFailure(String saslError) { public SASLFailure(String saslError) {
this(saslError, null); this(saslError, null);
} }
public SASLFailure(String saslError, Map<String, String> descriptiveTexts) { public SASLFailure(String saslError, Map<String, String> descriptiveTexts) {
if (descriptiveTexts != null) { super(descriptiveTexts);
this.descriptiveTexts = descriptiveTexts;
} else {
this.descriptiveTexts = Collections.emptyMap();
}
SASLError error = SASLError.fromString(saslError); SASLError error = SASLError.fromString(saslError);
if (error == null) { if (error == null) {
// RFC6120 6.5 states that unknown condition must be treat as generic authentication // RFC6120 6.5 states that unknown condition must be treat as generic authentication
@ -203,50 +198,19 @@ public class SaslStreamElements {
return saslErrorString; 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 @Override
public XmlStringBuilder toXML() { public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(); XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket(); xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
xml.emptyElement(saslErrorString); xml.emptyElement(saslErrorString);
for (Map.Entry<String, String> entry : descriptiveTexts.entrySet()) { addDescriptiveTextsAndExtensions(xml);
String xmllang = entry.getKey();
String text = entry.getValue();
xml.halfOpenElement("text").xmllangAttribute(xmllang).rightAngleBracket();
xml.escape(text);
xml.closeElement("text");
}
xml.closeElement(ELEMENT); xml.closeElement(ELEMENT);
return xml; 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.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -762,6 +761,18 @@ public class PacketParserUtils {
return new Compress.Feature(methods); 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. * Parses SASL authentication error packets.
* *
@ -773,17 +784,14 @@ public class PacketParserUtils {
public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException { public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException {
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
String condition = null; String condition = null;
Map<String, String> descriptiveTexts = new HashMap<String, String>(); Map<String, String> descriptiveTexts = null;
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); int eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case XmlPullParser.START_TAG:
String name = parser.getName(); String name = parser.getName();
if (name.equals("text")) { if (name.equals("text")) {
String xmllang = getLanguageAttribute(parser); descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
String text = parser.nextText();
String previousValue = descriptiveTexts.put(xmllang, text);
assert(previousValue == null);
} }
else { else {
assert(condition == null); assert(condition == null);
@ -806,37 +814,51 @@ public class PacketParserUtils {
* @param parser the XML parser. * @param parser the XML parser.
* @return an stream error packet. * @return an stream error packet.
* @throws XmlPullParserException if an exception occurs while parsing the packet. * @throws XmlPullParserException if an exception occurs while parsing the packet.
* @throws SmackException
*/ */
public static StreamError parseStreamError(XmlPullParser parser) throws IOException, public static StreamError parseStreamError(XmlPullParser parser) throws IOException, XmlPullParserException,
XmlPullParserException { SmackException {
final int depth = parser.getDepth(); final int initialDepth = parser.getDepth();
boolean done = false; List<PacketExtension> extensions = new ArrayList<PacketExtension>();
String code = null; Map<String, String> descriptiveTexts = null;
String text = null; StreamError.Condition condition = null;
while (!done) { String conditionText = null;
int eventType = parser.next(); outerloop: while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) { switch (eventType) {
String namespace = parser.getNamespace(); case XmlPullParser.START_TAG:
if (StreamError.NAMESPACE.equals(namespace)) {
String name = parser.getName(); String name = parser.getName();
if (name.equals(Packet.TEXT) && !parser.isEmptyElementTag()) { String namespace = parser.getNamespace();
parser.next(); switch (namespace) {
text = parser.getText(); 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 { break;
// If it's not a text element, that is qualified by the StreamError.NAMESPACE, case XmlPullParser.END_TAG:
// then it has to be the stream error code if (parser.getDepth() == initialDepth) {
code = name; break outerloop;
} }
break;
} }
} }
else if (eventType == XmlPullParser.END_TAG && depth == parser.getDepth()) { return new StreamError(condition, conditionText, descriptiveTexts, extensions);
done = true;
}
} }
return new StreamError(code, text);
}
/** /**
* Parses error sub-packets. * Parses error sub-packets.
@ -849,50 +871,47 @@ public class PacketParserUtils {
*/ */
public static XMPPError parseError(XmlPullParser parser) public static XMPPError parseError(XmlPullParser parser)
throws XmlPullParserException, IOException, SmackException { throws XmlPullParserException, IOException, SmackException {
String type = null; final int initialDepth = parser.getDepth();
String message = null; Map<String, String> descriptiveTexts = null;
String condition = null; XMPPError.Condition condition = null;
String conditionText = null;
List<PacketExtension> extensions = new ArrayList<PacketExtension>(); List<PacketExtension> extensions = new ArrayList<PacketExtension>();
// Parse the error header // Parse the error header
type = parser.getAttributeValue("", "type"); XMPPError.Type errorType = XMPPError.Type.fromString(parser.getAttributeValue("", "type"));
// Parse the text and condition tags String errorGenerator = parser.getAttributeValue("", "by");
outerloop:
while (true) { outerloop: while (true) {
int eventType = parser.next(); int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) { switch (eventType) {
if (parser.getName().equals(Packet.TEXT)) { case XmlPullParser.START_TAG:
message = parser.nextText(); 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 { break;
// Condition tag, it can be xmpp error or an application defined error. case XmlPullParser.END_TAG:
String elementName = parser.getName(); if (parser.getDepth() == initialDepth) {
String namespace = parser.getNamespace(); break outerloop;
if (namespace.equals(XMPPError.NAMESPACE)) {
condition = elementName;
}
else {
PacketParserUtils.addPacketExtension(extensions, parser, elementName, namespace);
}
} }
} }
else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("error")) {
break outerloop;
}
}
} }
// Parse the error type. return new XMPPError(condition, conditionText, errorGenerator, errorType, descriptiveTexts, extensions);
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);
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -113,7 +113,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse)); assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(), assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition()); ((IQ) targetResponse).getError().getCondition());
} }
@ -157,7 +157,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse)); assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(), assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition()); ((IQ) targetResponse).getError().getCondition());
} }
@ -205,7 +205,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse)); assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(), assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition()); ((IQ) targetResponse).getError().getCondition());
} }
@ -297,7 +297,7 @@ public class Socks5ByteStreamRequestTest {
assertTrue(IQ.class.isInstance(targetResponse)); assertTrue(IQ.class.isInstance(targetResponse));
assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(initiatorJID, targetResponse.getTo());
assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType());
assertEquals(XMPPError.Condition.item_not_found.toString(), assertEquals(XMPPError.Condition.item_not_found,
((IQ) targetResponse).getError().getCondition()); ((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.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener; import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleMediaListener; 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) { public IQ createJingleError(IQ iq, JingleError jingleError) {
IQ errorPacket = null; IQ errorPacket = null;
if (jingleError != null) { if (jingleError != null) {
List<PacketExtension> extList = new ArrayList<PacketExtension>(); // TODO This is wrong according to XEP-166 § 10, but this jingle implementation is deprecated anyways
extList.add(jingleError); XMPPError error = new XMPPError(XMPPError.Condition.undefined_condition, jingleError);
XMPPError error = new XMPPError(XMPPError.Type.CANCEL, jingleError.toString(), "", extList);
errorPacket = IQ.createErrorResponse(iq, error); errorPacket = IQ.createErrorResponse(iq, error);

View File

@ -113,7 +113,7 @@ public class MediaNegotiator extends JingleNegotiator {
setNegotiatorState(JingleNegotiatorState.FAILED); setNegotiatorState(JingleNegotiatorState.FAILED);
triggerMediaClosed(getBestCommonAudioPt()); triggerMediaClosed(getBestCommonAudioPt());
// This next line seems wrong, and may subvert the normal closing process. // 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)) { } else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs // Process ACKs
if (isExpectedId(iq.getPacketID())) { if (isExpectedId(iq.getPacketID())) {

View File

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

View File

@ -1101,7 +1101,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
break; break;
case Failed.ELEMENT: case Failed.ELEMENT:
Failed failed = ParseStreamManagement.failed(parser); Failed failed = ParseStreamManagement.failed(parser);
XMPPError xmppError = failed.getXMPPError(); XMPPError xmppError = new XMPPError(failed.getXMPPErrorCondition());
XMPPException xmppException = new XMPPErrorException("Stream Management failed", xmppError); XMPPException xmppException = new XMPPErrorException("Stream Management failed", xmppError);
// If only XEP-198 would specify different failure elements for the SM // 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 // 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 class Failed extends FullStreamElement {
public static final String ELEMENT = "failed"; public static final String ELEMENT = "failed";
private XMPPError error; private XMPPError.Condition condition;
public Failed() { public Failed() {
} }
public Failed(XMPPError error) { public Failed(XMPPError.Condition condition) {
this.error = error; this.condition = condition;
} }
public XMPPError getXMPPError() { public XMPPError.Condition getXMPPErrorCondition() {
return error; return condition;
} }
@Override @Override
public CharSequence toXML() { public CharSequence toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this); XmlStringBuilder xml = new XmlStringBuilder(this);
if (error != null) { if (condition != null) {
xml.rightAngleBracket(); xml.rightAngleBracket();
xml.append(error.toXML()); xml.append(condition.toString());
xml.xmlnsAttribute(XMPPError.NAMESPACE);
xml.closeElement(ELEMENT); xml.closeElement(ELEMENT);
} }
else { else {

View File

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

View File

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