mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-11 11:19:41 +02:00
Make XMPPError imutable and add stanza reference
This commit is contained in:
parent
83eda4c58d
commit
45feaecdf7
30 changed files with 308 additions and 94 deletions
|
@ -1050,8 +1050,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
// If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an
|
||||
// IQ of type 'error' with condition 'service-unavailable'.
|
||||
ErrorIQ errorIQ = IQ.createErrorResponse(iq, new XMPPError(
|
||||
XMPPError.Condition.service_unavailable));
|
||||
ErrorIQ errorIQ = IQ.createErrorResponse(iq, XMPPError.getBuilder((
|
||||
XMPPError.Condition.service_unavailable)));
|
||||
try {
|
||||
sendStanza(errorIQ);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ public abstract class XMPPException extends Exception {
|
|||
private static final long serialVersionUID = 212790389529249604L;
|
||||
private final XMPPError error;
|
||||
|
||||
public XMPPErrorException(XMPPError.Builder xmppErrorBuilder) {
|
||||
this(xmppErrorBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with the XMPPError that was the root case of the exception.
|
||||
*
|
||||
|
@ -90,7 +94,9 @@ public abstract class XMPPException extends Exception {
|
|||
* @param message a description of the exception.
|
||||
* @param error the root cause of the exception.
|
||||
* @param wrappedThrowable the root cause of the exception.
|
||||
* @deprecated use {@link XMPPErrorException(XMPPError)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public XMPPErrorException(String message, XMPPError error, Throwable wrappedThrowable) {
|
||||
super(message, wrappedThrowable);
|
||||
this.error = error;
|
||||
|
@ -102,7 +108,9 @@ public abstract class XMPPException extends Exception {
|
|||
*
|
||||
* @param message a description of the exception.
|
||||
* @param error the root cause of the exception.
|
||||
* @deprecated use {@link XMPPErrorException(XMPPError)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public XMPPErrorException(String message, XMPPError error) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Florian Schmaus
|
||||
* Copyright 2014-2015 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -26,9 +28,9 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
|
||||
public class AbstractError {
|
||||
|
||||
private final String textNamespace;
|
||||
protected final String textNamespace;
|
||||
protected final Map<String, String> descriptiveTexts;
|
||||
private final List<ExtensionElement> extensions;
|
||||
protected final List<ExtensionElement> extensions;
|
||||
|
||||
|
||||
protected AbstractError(Map<String, String> descriptiveTexts) {
|
||||
|
@ -108,4 +110,53 @@ public class AbstractError {
|
|||
xml.append(packetExtension.toXML());
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Builder<B extends Builder<B>> {
|
||||
protected String textNamespace;
|
||||
protected Map<String, String> descriptiveTexts;
|
||||
protected List<ExtensionElement> extensions;
|
||||
|
||||
public B setDescriptiveTexts(Map<String, String> descriptiveTexts) {
|
||||
if (this.descriptiveTexts == null) {
|
||||
this.descriptiveTexts = descriptiveTexts;
|
||||
}
|
||||
else {
|
||||
this.descriptiveTexts.putAll(descriptiveTexts);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B setDescriptiveEnText(String descriptiveEnText) {
|
||||
if (descriptiveTexts == null) {
|
||||
descriptiveTexts = new HashMap<>();
|
||||
}
|
||||
descriptiveTexts.put("en", descriptiveEnText);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B setTextNamespace(String textNamespace) {
|
||||
this.textNamespace = textNamespace;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B setExtensions(List<ExtensionElement> extensions) {
|
||||
if (this.extensions == null) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
else {
|
||||
this.extensions.addAll(extensions);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B addExtension(ExtensionElement extension) {
|
||||
if (extensions == null) {
|
||||
extensions = new ArrayList<>();
|
||||
}
|
||||
extensions.add(extension);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
protected abstract B getThis();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,13 @@ public class ErrorIQ extends SimpleIQ {
|
|||
* <p>
|
||||
* According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an <error/> child element.", so the xmppError argument is mandatory.
|
||||
* </p>
|
||||
* @param xmppError the XMPPError (required).
|
||||
* @param xmppErrorBuilder the XMPPError builder (required).
|
||||
*/
|
||||
public ErrorIQ(XMPPError xmppError) {
|
||||
public ErrorIQ(XMPPError.Builder xmppErrorBuilder) {
|
||||
super(ELEMENT, null);
|
||||
Objects.requireNonNull(xmppError, "XMPPError must not be null");
|
||||
Objects.requireNonNull(xmppErrorBuilder, "xmppErrorBuilder must not be null");
|
||||
setType(IQ.Type.error);
|
||||
setError(xmppError);
|
||||
setError(xmppErrorBuilder);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ public abstract class IQ extends Stanza {
|
|||
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
|
||||
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
|
||||
*/
|
||||
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
|
||||
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Builder error) {
|
||||
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
|
||||
throw new IllegalArgumentException(
|
||||
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
|
||||
|
@ -250,9 +250,40 @@ public abstract class IQ extends Stanza {
|
|||
result.setStanzaId(request.getStanzaId());
|
||||
result.setFrom(request.getTo());
|
||||
result.setTo(request.getFrom());
|
||||
|
||||
error.setStanza(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Condition condition) {
|
||||
return createErrorResponse(request, XMPPError.getBuilder(condition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to create a new {@link Type#error IQ.Type.error} IQ
|
||||
* based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
|
||||
* IQ. The new stanza(/packet) will be initialized with:<ul>
|
||||
* <li>The sender set to the recipient of the originating IQ.
|
||||
* <li>The recipient set to the sender of the originating IQ.
|
||||
* <li>The type set to {@link Type#error IQ.Type.error}.
|
||||
* <li>The id set to the id of the originating IQ.
|
||||
* <li>The child element contained in the associated originating IQ.
|
||||
* <li>The provided {@link XMPPError XMPPError}.
|
||||
* </ul>
|
||||
*
|
||||
* @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
|
||||
* @param error the error to associate with the created IQ packet.
|
||||
* @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of
|
||||
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
|
||||
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
|
||||
* @deprecated use {@link #createErrorResponse(IQ, org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
|
||||
return createErrorResponse(request, XMPPError.getBuilder(error));
|
||||
}
|
||||
|
||||
/**
|
||||
* A enum to represent the type of the IQ stanza.
|
||||
*/
|
||||
|
|
|
@ -248,11 +248,26 @@ public abstract class Stanza implements TopLevelStreamElement {
|
|||
* Sets the error for this packet.
|
||||
*
|
||||
* @param error the error to associate with this packet.
|
||||
* @deprecated use {@link #setError(org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setError(XMPPError error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error for this stanza.
|
||||
*
|
||||
* @param xmppErrorBuilder the error to associate with this stanza.
|
||||
*/
|
||||
public void setError(XMPPError.Builder xmppErrorBuilder) {
|
||||
if (xmppErrorBuilder == null) {
|
||||
return;
|
||||
}
|
||||
xmppErrorBuilder.setStanza(this);
|
||||
error = xmppErrorBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xml:lang of this Stanza, or null if one has not been set.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 2015 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
|
@ -91,17 +92,37 @@ public class XMPPError extends AbstractError {
|
|||
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 Stanza stanza;
|
||||
|
||||
// TODO: Deprecated constructors
|
||||
// deprecate in 4.3
|
||||
|
||||
/**
|
||||
* Create a new XMPPError.
|
||||
*
|
||||
* @param condition
|
||||
* @deprecated use {@link Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public XMPPError(Condition condition) {
|
||||
this(condition, null, null, null, null, null);
|
||||
this(condition, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new XMPPError.
|
||||
*
|
||||
* @param condition
|
||||
* @param applicationSpecificCondition
|
||||
* @deprecated use {@link Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public XMPPError(Condition condition, ExtensionElement applicationSpecificCondition) {
|
||||
this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition));
|
||||
this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,11 +135,31 @@ public class XMPPError extends AbstractError {
|
|||
* @param condition the error condition.
|
||||
* @param descriptiveTexts
|
||||
* @param extensions list of stanza(/packet) extensions
|
||||
* @deprecated use {@link Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
|
||||
List<ExtensionElement> extensions) {
|
||||
this(condition, conditionText, errorGenerator, type, descriptiveTexts, extensions, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error with the specified type, condition and message.
|
||||
* This constructor is used when the condition is not recognized automatically by XMPPError
|
||||
* i.e. there is not a defined instance of ErrorCondition or it does not apply the default
|
||||
* specification.
|
||||
*
|
||||
* @param type the error type.
|
||||
* @param condition the error condition.
|
||||
* @param descriptiveTexts
|
||||
* @param extensions list of stanza(/packet) extensions
|
||||
* @param stanza the stanza carrying this XMPP error.
|
||||
*/
|
||||
public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
|
||||
List<ExtensionElement> extensions, Stanza stanza) {
|
||||
super(descriptiveTexts, NAMESPACE, extensions);
|
||||
this.condition = condition;
|
||||
this.condition = Objects.requireNonNull(condition, "condition must not be null");
|
||||
this.stanza = stanza;
|
||||
// Some implementations may send the condition as non-empty element containing the empty string, that is
|
||||
// <condition xmlns='foo'></condition>, in this case the parser may calls this constructor with the empty string
|
||||
// as conditionText, therefore reset it to null if it's the empty string
|
||||
|
@ -176,6 +217,16 @@ public class XMPPError extends AbstractError {
|
|||
return conditionText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stanza carrying the XMPP error.
|
||||
*
|
||||
* @return the stanza carrying the XMPP error.
|
||||
* @since 4.2
|
||||
*/
|
||||
public Stanza getStanza() {
|
||||
return stanza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("XMPPError: ");
|
||||
|
@ -215,12 +266,81 @@ public class XMPPError extends AbstractError {
|
|||
return xml;
|
||||
}
|
||||
|
||||
public static XMPPError from(Condition condition, String descriptiveText) {
|
||||
public static XMPPError.Builder 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);
|
||||
return getBuilder().setCondition(condition).setDescriptiveTexts(descriptiveTexts);
|
||||
}
|
||||
|
||||
public static Builder getBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static Builder getBuilder(Condition condition) {
|
||||
return getBuilder().setCondition(condition);
|
||||
}
|
||||
|
||||
public static Builder getBuilder(XMPPError xmppError) {
|
||||
return getBuilder().copyFrom(xmppError);
|
||||
}
|
||||
|
||||
public static final class Builder extends AbstractError.Builder<Builder> {
|
||||
private Condition condition;
|
||||
private String conditionText;
|
||||
private String errorGenerator;
|
||||
private Type type;
|
||||
private Stanza stanza;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder setCondition(Condition condition) {
|
||||
this.condition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setType(Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setConditionText(String conditionText) {
|
||||
this.conditionText = conditionText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setErrorGenerator(String errorGenerator) {
|
||||
this.errorGenerator = errorGenerator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStanza(Stanza stanza) {
|
||||
this.stanza = stanza;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder copyFrom(XMPPError xmppError) {
|
||||
setCondition(xmppError.getCondition());
|
||||
setType(xmppError.getType());
|
||||
setConditionText(xmppError.getConditionText());
|
||||
setErrorGenerator(xmppError.getErrorGenerator());
|
||||
setStanza(xmppError.getStanza());
|
||||
setDescriptiveTexts(xmppError.descriptiveTexts);
|
||||
setTextNamespace(xmppError.textNamespace);
|
||||
setExtensions(xmppError.extensions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMPPError build() {
|
||||
return new XMPPError(condition, conditionText, errorGenerator, type, descriptiveTexts,
|
||||
extensions, stanza);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A class to represent the type of the Error. The types are:
|
||||
*
|
||||
|
|
|
@ -606,7 +606,7 @@ public class PacketParserUtils {
|
|||
ParserUtils.assertAtStartTag(parser);
|
||||
final int initialDepth = parser.getDepth();
|
||||
IQ iqPacket = null;
|
||||
XMPPError error = null;
|
||||
XMPPError.Builder error = null;
|
||||
|
||||
final String id = parser.getAttributeValue("", "id");
|
||||
final Jid to = ParserUtils.getJidAttribute(parser, "to");
|
||||
|
@ -846,17 +846,16 @@ public class PacketParserUtils {
|
|||
* @return an error sub-packet.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static XMPPError parseError(XmlPullParser parser)
|
||||
public static XMPPError.Builder parseError(XmlPullParser parser)
|
||||
throws Exception {
|
||||
final int initialDepth = parser.getDepth();
|
||||
Map<String, String> descriptiveTexts = null;
|
||||
XMPPError.Condition condition = null;
|
||||
String conditionText = null;
|
||||
List<ExtensionElement> extensions = new ArrayList<ExtensionElement>();
|
||||
XMPPError.Builder builder = XMPPError.getBuilder();
|
||||
|
||||
// Parse the error header
|
||||
XMPPError.Type errorType = XMPPError.Type.fromString(parser.getAttributeValue("", "type"));
|
||||
String errorGenerator = parser.getAttributeValue("", "by");
|
||||
builder.setType(XMPPError.Type.fromString(parser.getAttributeValue("", "type")));
|
||||
builder.setErrorGenerator(parser.getAttributeValue("", "by"));
|
||||
|
||||
outerloop: while (true) {
|
||||
int eventType = parser.next();
|
||||
|
@ -871,9 +870,9 @@ public class PacketParserUtils {
|
|||
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
|
||||
break;
|
||||
default:
|
||||
condition = XMPPError.Condition.fromString(name);
|
||||
builder.setCondition(XMPPError.Condition.fromString(name));
|
||||
if (!parser.isEmptyElementTag()) {
|
||||
conditionText = parser.nextText();
|
||||
builder.setConditionText(parser.nextText());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -888,7 +887,8 @@ public class PacketParserUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
return new XMPPError(condition, conditionText, errorGenerator, errorType, descriptiveTexts, extensions);
|
||||
builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,7 +62,7 @@ public class IQResponseTest {
|
|||
*/
|
||||
@Test
|
||||
public void testGeneratingValidErrorResponse() throws XmppStringprepException {
|
||||
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request);
|
||||
final XMPPError.Builder error = XMPPError.getBuilder(XMPPError.Condition.bad_request);
|
||||
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
|
||||
|
||||
request.setType(IQ.Type.set);
|
||||
|
@ -75,7 +75,7 @@ public class IQResponseTest {
|
|||
assertNotNull(result.getStanzaId());
|
||||
assertEquals(request.getStanzaId(), result.getStanzaId());
|
||||
assertEquals(request.getFrom(), result.getTo());
|
||||
assertEquals(error, result.getError());
|
||||
assertEquals(error.build().toXML(), result.getError().toXML());
|
||||
// TODO this test was never valid
|
||||
// assertEquals(CHILD_ELEMENT, result.getChildElementXML());
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class IQResponseTest {
|
|||
*/
|
||||
@Test
|
||||
public void testGeneratingErrorBasedOnError() throws XmppStringprepException {
|
||||
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request);
|
||||
final XMPPError.Builder error = XMPPError.getBuilder(XMPPError.Condition.bad_request);
|
||||
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
|
||||
|
||||
request.setType(IQ.Type.error);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue