1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-29 15:32:06 +01:00

Compare commits

...

16 commits

Author SHA1 Message Date
Florian Schmaus
9d626bf787 core: improve AsyncButOrdered
Instead of marking the handle as not running by setting the handler's
value in the map to false, we now remove simply the key if there is no
handler running. This also means we no longer need to use a weak hash
map for this.

Also reduce the size of the synchronized blocks, mainly by scheduling
the handler outside of the synchronized(threadActiveMap) block.

Make some code better readable and add some more comments. Also do
start a new handler thread if the task threw.
2019-11-08 10:14:21 +01:00
Florian Schmaus
a7a298c5d8 Use standard stanza listeners in MultiUserChat
Those, relatively new, listeners guarantee that the individual
listeners are not invoked in concurrently while preserving the
order. Exactly what MultiUserChat previously did with AsyncButOrdered,
which is now no longer needed and hence can be removed.
2019-11-07 14:36:50 +01:00
Florian Schmaus
eb4c2c5572 s/occured/occurred/ 2019-10-30 12:02:36 +01:00
Florian Schmaus
b0277d7e74 Emit <starttls/> as empty element when possible 2019-10-30 12:00:20 +01:00
Florian Schmaus
2915101843 Use isEmpty() in StringUtils.requireNullOrNotEmpty() 2019-10-30 11:56:55 +01:00
Florian Schmaus
6d1252755b Mark more methods in Stanza as final 2019-10-30 09:23:01 +01:00
Florian Schmaus
ee699f24dd Remove DiscoverInfo.Identity(Identity) copy constructor
as there is no need to copy an immutable type.
2019-10-30 08:24:13 +01:00
Florian Schmaus
63a4212f7e Remove clone() from DiscoverInfo.Identity
there is no need to clone immutable types.
2019-10-30 08:22:47 +01:00
Florian Schmaus
63ba524758 Make IQ(IQ) constructor protected 2019-10-30 08:20:25 +01:00
Florian Schmaus
e2223254cf Organize imports everywhere 2019-10-29 21:12:22 +01:00
Florian Schmaus
69767e9538 Rename AbstractIqBuilder subclasses to their designated names 2019-10-29 19:34:18 +01:00
Florian Schmaus
6e32305987 Apply builder pattern to DiscoverInfo
This is the first transformation of an IQ type to the builder type.
2019-10-29 11:14:55 +01:00
Florian Schmaus
36072fb25a test: created mocked connectionwith stanza factory 2019-10-29 10:40:41 +01:00
Florian Schmaus
4b01892129 Remove unused DiscoverInfo instances in PingTest 2019-10-27 07:51:25 +01:00
Florian Schmaus
e2d206e741 Introduce XMPPConnection.add(Message|Presence)Interceptor
add deprecate addStanzaInterceptor().
2019-10-25 21:41:55 +02:00
Florian Schmaus
5db6191110 Introduce StanzaBuilder
As first step to immutable Stanza types.
2019-10-25 21:41:55 +02:00
234 changed files with 4056 additions and 1411 deletions

View file

@ -15,7 +15,11 @@ Smack Key Advantages
AbstractXMPPConnection connection = new XMPPTCPConnection("mtucker", "password", "jabber.org"); AbstractXMPPConnection connection = new XMPPTCPConnection("mtucker", "password", "jabber.org");
connection.connect().login(); connection.connect().login();
Message message = new Message("jsmith@jivesoftware.com", "Howdy! How are you?"); Message message = connection.getStanzaFactory()
.buildMessageStanza()
.to("jsmith@jivesoftware.com")
.setBody("Howdy! How are you?")
.build();
connection.sendStanza(message); connection.sendStanza(message);
``` ```

View file

@ -28,17 +28,17 @@ SMACK_EXCEPTIONS[InterruptedException]="if the calling thread was interrupted."
SMACK_EXCEPTIONS[XMPPErrorException]="if there was an XMPP error returned." SMACK_EXCEPTIONS[XMPPErrorException]="if there was an XMPP error returned."
SMACK_EXCEPTIONS[NoResponseException]="if there was no response from the remote entity." SMACK_EXCEPTIONS[NoResponseException]="if there was no response from the remote entity."
SMACK_EXCEPTIONS[NotLoggedInException]="if the XMPP connection is not authenticated." SMACK_EXCEPTIONS[NotLoggedInException]="if the XMPP connection is not authenticated."
SMACK_EXCEPTIONS[BOSHException]="if an BOSH related error occured." SMACK_EXCEPTIONS[BOSHException]="if an BOSH related error occurred."
SMACK_EXCEPTIONS[IOException]="if an I/O error occured." SMACK_EXCEPTIONS[IOException]="if an I/O error occurred."
SMACK_EXCEPTIONS[SmackException]="if Smack detected an exceptional situation." SMACK_EXCEPTIONS[SmackException]="if Smack detected an exceptional situation."
SMACK_EXCEPTIONS[XMPPException]="if an XMPP protocol error was received." SMACK_EXCEPTIONS[XMPPException]="if an XMPP protocol error was received."
SMACK_EXCEPTIONS[SmackSaslException]="if a SASL specific error occured." SMACK_EXCEPTIONS[SmackSaslException]="if a SASL specific error occurred."
SMACK_EXCEPTIONS[SASLErrorException]="if a SASL protocol error was returned." SMACK_EXCEPTIONS[SASLErrorException]="if a SASL protocol error was returned."
SMACK_EXCEPTIONS[NotAMucServiceException]="if the entity is not a MUC serivce." SMACK_EXCEPTIONS[NotAMucServiceException]="if the entity is not a MUC serivce."
SMACK_EXCEPTIONS[NoSuchAlgorithmException]="if no such algorithm is available." SMACK_EXCEPTIONS[NoSuchAlgorithmException]="if no such algorithm is available."
SMACK_EXCEPTIONS[KeyManagementException]="if there was a key mangement error." SMACK_EXCEPTIONS[KeyManagementException]="if there was a key mangement error."
SMACK_EXCEPTIONS[XmppStringprepException]="if the provided string is invalid." SMACK_EXCEPTIONS[XmppStringprepException]="if the provided string is invalid."
SMACK_EXCEPTIONS[XmlPullParserException]="if an error in the XML parser occured." SMACK_EXCEPTIONS[XmlPullParserException]="if an error in the XML parser occurred."
SMACK_EXCEPTIONS[SmackParsingException]="if the Smack parser (provider) encountered invalid input." SMACK_EXCEPTIONS[SmackParsingException]="if the Smack parser (provider) encountered invalid input."
SMACK_EXCEPTIONS[MucNotJoinedException]="if not joined to the Multi-User Chat." SMACK_EXCEPTIONS[MucNotJoinedException]="if not joined to the Multi-User Chat."
SMACK_EXCEPTIONS[MucAlreadyJoinedException]="if already joined the Multi-User Chat."7y SMACK_EXCEPTIONS[MucAlreadyJoinedException]="if already joined the Multi-User Chat."7y
@ -54,7 +54,7 @@ SMACK_EXCEPTIONS[IllegalArgumentException]="if an illegal argument was given."
SMACK_EXCEPTIONS[NotAPubSubNodeException]="if a involved node is not a PubSub node." SMACK_EXCEPTIONS[NotAPubSubNodeException]="if a involved node is not a PubSub node."
SMACK_EXCEPTIONS[NoAcceptableTransferMechanisms]="if no acceptable transfer mechanisms are available" SMACK_EXCEPTIONS[NoAcceptableTransferMechanisms]="if no acceptable transfer mechanisms are available"
SMACK_EXCEPTIONS[NoSuchMethodException]="if no such method is declared" SMACK_EXCEPTIONS[NoSuchMethodException]="if no such method is declared"
SMACK_EXCEPTIONS[Exception]="if an exception occured." SMACK_EXCEPTIONS[Exception]="if an exception occurred."
SMACK_EXCEPTIONS[TestNotPossibleException]="if the test is not possible." SMACK_EXCEPTIONS[TestNotPossibleException]="if the test is not possible."
SMACK_EXCEPTIONS[TimeoutException]="if there was a timeout." SMACK_EXCEPTIONS[TimeoutException]="if there was a timeout."
SMACK_EXCEPTIONS[IllegalStateException]="if an illegal state was encountered" SMACK_EXCEPTIONS[IllegalStateException]="if an illegal state was encountered"

View file

@ -517,8 +517,8 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) { if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
throw new StreamErrorException(PacketParserUtils.parseStreamError(parser)); throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
} else { } else {
StanzaError.Builder builder = PacketParserUtils.parseError(parser); StanzaError stanzaError = PacketParserUtils.parseError(parser);
throw new XMPPException.XMPPErrorException(null, builder.build()); throw new XMPPException.XMPPErrorException(null, stanzaError);
} }
default: default:
parseAndProcessNonza(parser); parseAndProcessNonza(parser);

View file

@ -100,16 +100,22 @@ import org.jivesoftware.smack.packet.FullyQualifiedElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Mechanisms; import org.jivesoftware.smack.packet.Mechanisms;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.MessageOrPresence;
import org.jivesoftware.smack.packet.MessageOrPresenceBuilder;
import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.PresenceBuilder;
import org.jivesoftware.smack.packet.Session; import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StanzaFactory;
import org.jivesoftware.smack.packet.StartTls; import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError; import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback; import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
@ -121,11 +127,13 @@ import org.jivesoftware.smack.sasl.core.SASLAnonymous;
import org.jivesoftware.smack.sasl.packet.SaslNonza; import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.CollectionUtil;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.Predicate;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.dns.HostAddress; import org.jivesoftware.smack.util.dns.HostAddress;
import org.jivesoftware.smack.util.dns.SmackDaneProvider; import org.jivesoftware.smack.util.dns.SmackDaneProvider;
@ -241,6 +249,10 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private final Map<StanzaListener, InterceptorWrapper> interceptors = private final Map<StanzaListener, InterceptorWrapper> interceptors =
new HashMap<>(); new HashMap<>();
private final Map<Consumer<MessageBuilder>, GenericInterceptorWrapper<MessageBuilder, Message>> messageInterceptors = new HashMap<>();
private final Map<Consumer<PresenceBuilder>, GenericInterceptorWrapper<PresenceBuilder, Presence>> presenceInterceptors = new HashMap<>();
private XmlEnvironment incomingStreamXmlEnvironment; private XmlEnvironment incomingStreamXmlEnvironment;
protected XmlEnvironment outgoingStreamXmlEnvironment; protected XmlEnvironment outgoingStreamXmlEnvironment;
@ -402,6 +414,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>();
private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>();
private final StanzaFactory stanzaFactory;
/** /**
* Create a new XMPPConnection to an XMPP server. * Create a new XMPPConnection to an XMPP server.
* *
@ -440,6 +454,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) { for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) {
listener.connectionCreated(this); listener.connectionCreated(this);
} }
StanzaIdSource stanzaIdSource = configuration.constructStanzaIdSource();
stanzaFactory = new StanzaFactory(stanzaIdSource);
} }
/** /**
@ -518,7 +535,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* *
* @throws XMPPException if an error occurs on the XMPP protocol level. * @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somewhere else besides XMPP protocol level. * @throws SmackException if an error occurs somewhere else besides XMPP protocol level.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @return a reference to this object, to chain <code>connect()</code> with <code>login()</code>. * @return a reference to this object, to chain <code>connect()</code> with <code>login()</code>.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
*/ */
@ -558,7 +575,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* login if the previous connection state was logged (authenticated). * login if the previous connection state was logged (authenticated).
* *
* @throws SmackException if Smack detected an exceptional situation. * @throws SmackException if Smack detected an exceptional situation.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XMPPException if an XMPP protocol error was received. * @throws XMPPException if an XMPP protocol error was received.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
*/ */
@ -611,7 +628,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* @param password TODO javadoc me please * @param password TODO javadoc me please
* @throws XMPPException if an XMPP protocol error was received. * @throws XMPPException if an XMPP protocol error was received.
* @throws SmackException if Smack detected an exceptional situation. * @throws SmackException if Smack detected an exceptional situation.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @see #login * @see #login
*/ */
@ -629,7 +646,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* @param resource TODO javadoc me please * @param resource TODO javadoc me please
* @throws XMPPException if an XMPP protocol error was received. * @throws XMPPException if an XMPP protocol error was received.
* @throws SmackException if Smack detected an exceptional situation. * @throws SmackException if Smack detected an exceptional situation.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @see #login * @see #login
*/ */
@ -733,7 +750,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// eventually load the roster. And we should load the roster before we // eventually load the roster. And we should load the roster before we
// send the initial presence. // send the initial presence.
if (config.isSendPresence() && !resumed) { if (config.isSendPresence() && !resumed) {
sendStanza(new Presence(Presence.Type.available)); Presence availablePresence = getStanzaFactory()
.buildPresenceStanza()
.ofType(Presence.Type.available)
.build();
sendStanza(availablePresence);
} }
} }
@ -814,6 +835,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
@Override
public final StanzaFactory getStanzaFactory() {
return stanzaFactory;
}
@Override @Override
public final void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException { public final void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
Objects.requireNonNull(stanza, "Stanza must not be null"); Objects.requireNonNull(stanza, "Stanza must not be null");
@ -833,8 +859,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
// Invoke interceptors for the new stanza that is about to be sent. Interceptors may modify // Invoke interceptors for the new stanza that is about to be sent. Interceptors may modify
// the content of the stanza. // the content of the stanza.
firePacketInterceptors(stanza); Stanza stanzaAfterInterceptors = firePacketInterceptors(stanza);
sendStanzaInternal(stanza); sendStanzaInternal(stanzaAfterInterceptors);
} }
/** /**
@ -847,9 +873,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* @return the used SASLMechanism. * @return the used SASLMechanism.
* @throws XMPPErrorException if there was an XMPP error returned. * @throws XMPPErrorException if there was an XMPP error returned.
* @throws SASLErrorException if a SASL protocol error was returned. * @throws SASLErrorException if a SASL protocol error was returned.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws NoResponseException if there was no response from the remote entity. * @throws NoResponseException if there was no response from the remote entity.
* @throws SmackWrappedException in case of an exception. * @throws SmackWrappedException in case of an exception.
@ -893,7 +919,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
public void disconnect() { public void disconnect() {
Presence unavailablePresence = null; Presence unavailablePresence = null;
if (isAuthenticated()) { if (isAuthenticated()) {
unavailablePresence = new Presence(Presence.Type.unavailable); unavailablePresence = getStanzaFactory().buildPresenceStanza()
.ofType(Presence.Type.unavailable)
.build();
} }
try { try {
disconnect(unavailablePresence); disconnect(unavailablePresence);
@ -1186,6 +1214,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}); });
} }
@Deprecated
@Override @Override
public void addStanzaInterceptor(StanzaListener packetInterceptor, public void addStanzaInterceptor(StanzaListener packetInterceptor,
StanzaFilter packetFilter) { StanzaFilter packetFilter) {
@ -1198,6 +1227,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
@Deprecated
@Override @Override
public void removeStanzaInterceptor(StanzaListener packetInterceptor) { public void removeStanzaInterceptor(StanzaListener packetInterceptor) {
synchronized (interceptors) { synchronized (interceptors) {
@ -1205,15 +1235,83 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
private static <MPB extends MessageOrPresenceBuilder<MP, MPB>, MP extends MessageOrPresence<MPB>> void addInterceptor(
Map<Consumer<MPB>, GenericInterceptorWrapper<MPB, MP>> interceptors, Consumer<MPB> interceptor,
Predicate<MP> filter) {
Objects.requireNonNull(interceptor, "Interceptor must not be null");
GenericInterceptorWrapper<MPB, MP> interceptorWrapper = new GenericInterceptorWrapper<>(interceptor, filter);
synchronized (interceptors) {
interceptors.put(interceptor, interceptorWrapper);
}
}
private static <MPB extends MessageOrPresenceBuilder<MP, MPB>, MP extends MessageOrPresence<MPB>> void removeInterceptor(
Map<Consumer<MPB>, GenericInterceptorWrapper<MPB, MP>> interceptors, Consumer<MPB> interceptor) {
synchronized (interceptors) {
interceptors.remove(interceptor);
}
}
@Override
public void addMessageInterceptor(Consumer<MessageBuilder> messageInterceptor, Predicate<Message> messageFilter) {
addInterceptor(messageInterceptors, messageInterceptor, messageFilter);
}
@Override
public void removeMessageInterceptor(Consumer<MessageBuilder> messageInterceptor) {
removeInterceptor(messageInterceptors, messageInterceptor);
}
@Override
public void addPresenceInterceptor(Consumer<PresenceBuilder> presenceInterceptor,
Predicate<Presence> presenceFilter) {
addInterceptor(presenceInterceptors, presenceInterceptor, presenceFilter);
}
@Override
public void removePresenceInterceptor(Consumer<PresenceBuilder> presenceInterceptor) {
removeInterceptor(presenceInterceptors, presenceInterceptor);
}
private static <MPB extends MessageOrPresenceBuilder<MP, MPB>, MP extends MessageOrPresence<MPB>> MP fireMessageOrPresenceInterceptors(
MP messageOrPresence, Map<Consumer<MPB>, GenericInterceptorWrapper<MPB, MP>> interceptors) {
List<Consumer<MPB>> interceptorsToInvoke = new LinkedList<>();
synchronized (interceptors) {
for (GenericInterceptorWrapper<MPB, MP> interceptorWrapper : interceptors.values()) {
if (interceptorWrapper.filterMatches(messageOrPresence)) {
Consumer<MPB> interceptor = interceptorWrapper.getInterceptor();
interceptorsToInvoke.add(interceptor);
}
}
}
// Avoid transforming the stanza to a builder if there is no interceptor.
if (interceptorsToInvoke.isEmpty()) {
return messageOrPresence;
}
MPB builder = messageOrPresence.asBuilder();
for (Consumer<MPB> interceptor : interceptorsToInvoke) {
interceptor.accept(builder);
}
// Now that the interceptors have (probably) modified the stanza in its builder form, we need to re-assemble it.
messageOrPresence = builder.build();
return messageOrPresence;
}
/** /**
* Process interceptors. Interceptors may modify the stanza that is about to be sent. * Process interceptors. Interceptors may modify the stanza that is about to be sent.
* Since the thread that requested to send the stanza will invoke all interceptors, it * Since the thread that requested to send the stanza will invoke all interceptors, it
* is important that interceptors perform their work as soon as possible so that the * is important that interceptors perform their work as soon as possible so that the
* thread does not remain blocked for a long period. * thread does not remain blocked for a long period.
* *
* @param packet the stanza that is going to be sent to the server * @param packet the stanza that is going to be sent to the server.
* @return the, potentially modified stanza, after the interceptors are run.
*/ */
private void firePacketInterceptors(Stanza packet) { private Stanza firePacketInterceptors(Stanza packet) {
List<StanzaListener> interceptorsToInvoke = new LinkedList<>(); List<StanzaListener> interceptorsToInvoke = new LinkedList<>();
synchronized (interceptors) { synchronized (interceptors) {
for (InterceptorWrapper interceptorWrapper : interceptors.values()) { for (InterceptorWrapper interceptorWrapper : interceptors.values()) {
@ -1229,6 +1327,22 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
LOGGER.log(Level.SEVERE, "Packet interceptor threw exception", e); LOGGER.log(Level.SEVERE, "Packet interceptor threw exception", e);
} }
} }
final Stanza stanzaAfterInterceptors;
if (packet instanceof Message) {
Message message = (Message) packet;
stanzaAfterInterceptors = fireMessageOrPresenceInterceptors(message, messageInterceptors);
}
else if (packet instanceof Presence) {
Presence presence = (Presence) packet;
stanzaAfterInterceptors = fireMessageOrPresenceInterceptors(presence, presenceInterceptors);
} else {
// We do not (yet) support interceptors for IQ stanzas.
assert packet instanceof IQ;
stanzaAfterInterceptors = packet;
}
return stanzaAfterInterceptors;
} }
/** /**
@ -1416,7 +1530,7 @@ 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 // 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'. // IQ of type 'error' with condition 'service-unavailable'.
final ErrorIQ errorIQ = IQ.createErrorResponse(iq, StanzaError.getBuilder( final ErrorIQ errorIQ = IQ.createErrorResponse(iq, StanzaError.getBuilder(
replyCondition)); replyCondition).build());
// Use async sendStanza() here, since if sendStanza() would block, then some connections, e.g. // Use async sendStanza() here, since if sendStanza() would block, then some connections, e.g.
// XmppNioTcpConnection, would deadlock, as this operation is performed in the same thread that is // XmppNioTcpConnection, would deadlock, as this operation is performed in the same thread that is
asyncGo(() -> { asyncGo(() -> {
@ -1656,6 +1770,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
/** /**
* A wrapper class to associate a stanza filter with an interceptor. * A wrapper class to associate a stanza filter with an interceptor.
*/ */
@Deprecated
// TODO: Remove once addStanzaInterceptor is gone.
protected static class InterceptorWrapper { protected static class InterceptorWrapper {
private final StanzaListener packetInterceptor; private final StanzaListener packetInterceptor;
@ -1681,6 +1797,24 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
private static final class GenericInterceptorWrapper<MPB extends MessageOrPresenceBuilder<MP, MPB>, MP extends MessageOrPresence<MPB>> {
private final Consumer<MPB> stanzaInterceptor;
private final Predicate<MP> stanzaFilter;
private GenericInterceptorWrapper(Consumer<MPB> stanzaInterceptor, Predicate<MP> stanzaFilter) {
this.stanzaInterceptor = stanzaInterceptor;
this.stanzaFilter = stanzaFilter;
}
private boolean filterMatches(MP stanza) {
return stanzaFilter == null || stanzaFilter.test(stanza);
}
public Consumer<MPB> getInterceptor() {
return stanzaInterceptor;
}
}
@Override @Override
public int getConnectionCounter() { public int getConnectionCounter() {
return connectionCounterValue; return connectionCounterValue;

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
*/ */
package org.jivesoftware.smack; package org.jivesoftware.smack;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@ -51,9 +52,17 @@ import java.util.concurrent.Executor;
*/ */
public class AsyncButOrdered<K> { public class AsyncButOrdered<K> {
/**
* A map with the currently pending runnables for a given key. Note that this is a weak hash map so we do not have
* to take care of removing the keys ourselfs from the map.
*/
private final Map<K, Queue<Runnable>> pendingRunnables = new WeakHashMap<>(); private final Map<K, Queue<Runnable>> pendingRunnables = new WeakHashMap<>();
private final Map<K, Boolean> threadActiveMap = new WeakHashMap<>(); /**
* A marker map if there is an active thread for the given key. Holds the responsible handler thread if one is
* active, otherwise the key is non-existend in the map.
*/
private final Map<K, Handler> threadActiveMap = new HashMap<>();
private final Executor executor; private final Executor executor;
@ -65,6 +74,14 @@ public class AsyncButOrdered<K> {
this.executor = executor; this.executor = executor;
} }
private void scheduleHandler(Handler handler) {
if (executor == null) {
AbstractXMPPConnection.asyncGo(handler);
} else {
executor.execute(handler);
}
}
/** /**
* Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key. * Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key.
* *
@ -73,6 +90,7 @@ public class AsyncButOrdered<K> {
* @return true if a new thread was created * @return true if a new thread was created
*/ */
public boolean performAsyncButOrdered(K key, Runnable runnable) { public boolean performAsyncButOrdered(K key, Runnable runnable) {
// First check if a key queue already exists, create one if not.
Queue<Runnable> keyQueue; Queue<Runnable> keyQueue;
synchronized (pendingRunnables) { synchronized (pendingRunnables) {
keyQueue = pendingRunnables.get(key); keyQueue = pendingRunnables.get(key);
@ -82,29 +100,27 @@ public class AsyncButOrdered<K> {
} }
} }
// Then add the task to the queue.
keyQueue.add(runnable); keyQueue.add(runnable);
boolean newHandler; // Finally check if there is already a handler working on that queue, create one if not.
Handler newlyCreatedHandler = null;
synchronized (threadActiveMap) { synchronized (threadActiveMap) {
Boolean threadActive = threadActiveMap.get(key); if (!threadActiveMap.containsKey(key)) {
if (threadActive == null) { newlyCreatedHandler = new Handler(keyQueue, key);
threadActive = false;
threadActiveMap.put(key, threadActive);
}
newHandler = !threadActive; // Mark that there is thread active for the given key. Note that this has to be done before scheduling
if (newHandler) { // the handler thread.
Handler handler = new Handler(keyQueue, key); threadActiveMap.put(key, newlyCreatedHandler);
threadActiveMap.put(key, true);
if (executor == null) {
AbstractXMPPConnection.asyncGo(handler);
} else {
executor.execute(handler);
}
} }
} }
return newHandler; if (newlyCreatedHandler != null) {
scheduleHandler(newlyCreatedHandler);
return true;
}
return false;
} }
public Executor asExecutorFor(final K key) { public Executor asExecutorFor(final K key) {
@ -134,11 +150,14 @@ public class AsyncButOrdered<K> {
try { try {
runnable.run(); runnable.run();
} catch (Throwable t) { } catch (Throwable t) {
// The run() method threw, this handler thread is going to terminate because of that. Ensure we note // The run() method threw, this handler thread is going to terminate because of that. We create
// that in the map. // a new handler to continue working on the queue while throwing the throwable so that the
// executor can handle it.
Handler newlyCreatedHandler = new Handler(keyQueue, key);
synchronized (threadActiveMap) { synchronized (threadActiveMap) {
threadActiveMap.put(key, false); threadActiveMap.put(key, newlyCreatedHandler);
} }
scheduleHandler(newlyCreatedHandler);
throw t; throw t;
} }
} }
@ -146,7 +165,7 @@ public class AsyncButOrdered<K> {
synchronized (threadActiveMap) { synchronized (threadActiveMap) {
// If the queue is empty, stop this handler, otherwise continue looping. // If the queue is empty, stop this handler, otherwise continue looping.
if (keyQueue.isEmpty()) { if (keyQueue.isEmpty()) {
threadActiveMap.put(key, false); threadActiveMap.remove(key);
break mainloop; break mainloop;
} }
} }

View file

@ -36,6 +36,9 @@ import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory; import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.core.SASLAnonymous; import org.jivesoftware.smack.sasl.core.SASLAnonymous;
@ -159,6 +162,8 @@ public abstract class ConnectionConfiguration {
private final boolean compressionEnabled; private final boolean compressionEnabled;
private final StanzaIdSourceFactory stanzaIdSourceFactory;
protected ConnectionConfiguration(Builder<?, ?> builder) { protected ConnectionConfiguration(Builder<?, ?> builder) {
authzid = builder.authzid; authzid = builder.authzid;
username = builder.username; username = builder.username;
@ -213,6 +218,8 @@ public abstract class ConnectionConfiguration {
compressionEnabled = builder.compressionEnabled; compressionEnabled = builder.compressionEnabled;
stanzaIdSourceFactory = builder.stanzaIdSourceFactory;
// If the enabledSaslmechanisms are set, then they must not be empty // If the enabledSaslmechanisms are set, then they must not be empty
assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty(); assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty();
@ -568,6 +575,10 @@ public abstract class ConnectionConfiguration {
return Collections.unmodifiableSet(enabledSaslMechanisms); return Collections.unmodifiableSet(enabledSaslMechanisms);
} }
StanzaIdSource constructStanzaIdSource() {
return stanzaIdSourceFactory.constructStanzaIdSource();
}
/** /**
* A builder for XMPP connection configurations. * A builder for XMPP connection configurations.
* <p> * <p>
@ -612,6 +623,7 @@ public abstract class ConnectionConfiguration {
private Set<String> enabledSaslMechanisms; private Set<String> enabledSaslMechanisms;
private X509TrustManager customX509TrustManager; private X509TrustManager customX509TrustManager;
private boolean compressionEnabled = false; private boolean compressionEnabled = false;
private StanzaIdSourceFactory stanzaIdSourceFactory = new StandardStanzaIdSource.Factory();
protected Builder() { protected Builder() {
if (SmackConfiguration.DEBUG) { if (SmackConfiguration.DEBUG) {
@ -1134,6 +1146,17 @@ public abstract class ConnectionConfiguration {
return getThis(); return getThis();
} }
/**
* Set the factory for stanza ID sources to use.
*
* @param stanzaIdSourceFactory the factory for stanza ID sources to use.
* @return a reference to this builder.
* @since 4.4
*/
public B setStanzaIdSourceFactory(StanzaIdSourceFactory stanzaIdSourceFactory) {
this.stanzaIdSourceFactory = Objects.requireNonNull(stanzaIdSourceFactory);
return getThis();
}
public abstract C build(); public abstract C build();

View file

@ -175,9 +175,9 @@ public final class SASLAuthentication {
* @return the used SASLMechanism. * @return the used SASLMechanism.
* @throws XMPPErrorException if there was an XMPP error returned. * @throws XMPPErrorException if there was an XMPP error returned.
* @throws SASLErrorException if a SASL protocol error was returned. * @throws SASLErrorException if a SASL protocol error was returned.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws NoResponseException if there was no response from the remote entity. * @throws NoResponseException if there was no response from the remote entity.
*/ */
@ -233,7 +233,7 @@ public final class SASLAuthentication {
* *
* @param challenge a base64 encoded string representing the challenge. * @param challenge a base64 encoded string representing the challenge.
* @param finalChallenge true if this is the last challenge send by the server within the success stanza * @param finalChallenge true if this is the last challenge send by the server within the success stanza
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
*/ */
@ -252,7 +252,7 @@ public final class SASLAuthentication {
* @throws SmackException if Smack detected an exceptional situation. * @throws SmackException if Smack detected an exceptional situation.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
*/ */
void authenticated(Success success) throws InterruptedException, SmackSaslException, NotConnectedException { void authenticated(Success success) throws InterruptedException, SmackSaslException, NotConnectedException {
// RFC6120 6.3.10 "At the end of the authentication exchange, the SASL server (the XMPP // RFC6120 6.3.10 "At the end of the authentication exchange, the SASL server (the XMPP

View file

@ -49,7 +49,6 @@ import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smack.util.FileUtils;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;

View file

@ -27,8 +27,15 @@ import org.jivesoftware.smack.iqrequest.IQRequestHandler;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.FullyQualifiedElement; import org.jivesoftware.smack.packet.FullyQualifiedElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.PresenceBuilder;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaFactory;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.Predicate;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
@ -178,6 +185,8 @@ public interface XMPPConnection {
*/ */
boolean isUsingCompression(); boolean isUsingCompression();
StanzaFactory getStanzaFactory();
/** /**
* Sends the specified stanza to the server. * Sends the specified stanza to the server.
* *
@ -358,7 +367,7 @@ public interface XMPPConnection {
boolean removeStanzaListener(StanzaListener stanzaListener); boolean removeStanzaListener(StanzaListener stanzaListener);
/** /**
* Registers a <b>synchronous</b> stanza listener with this connection. A stanza listener will be invoked only when * Registers a <b>synchronous</b> stanza listener with this connection. A stanza listener will be invoked only when
* an incoming stanza is received. A stanza filter determines which stanzas will be delivered to the listener. If * an incoming stanza is received. A stanza filter determines which stanzas will be delivered to the listener. If
* the same stanza listener is added again with a different filter, only the new filter will be used. * the same stanza listener is added again with a different filter, only the new filter will be used.
* <p> * <p>
@ -443,16 +452,65 @@ public interface XMPPConnection {
* *
* @param stanzaInterceptor the stanza interceptor to notify of stanzas about to be sent. * @param stanzaInterceptor the stanza interceptor to notify of stanzas about to be sent.
* @param stanzaFilter the stanza filter to use. * @param stanzaFilter the stanza filter to use.
* @deprecated use {@link #addMessageInterceptor(Consumer, Predicate)} or {@link #addPresenceInterceptor(Consumer, Predicate)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
void addStanzaInterceptor(StanzaListener stanzaInterceptor, StanzaFilter stanzaFilter); void addStanzaInterceptor(StanzaListener stanzaInterceptor, StanzaFilter stanzaFilter);
/** /**
* Removes a stanza interceptor. * Removes a stanza interceptor.
* *
* @param stanzaInterceptor the stanza interceptor to remove. * @param stanzaInterceptor the stanza interceptor to remove.
* @deprecated use {@link #removeMessageInterceptor(Consumer)} or {@link #removePresenceInterceptor(Consumer)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
void removeStanzaInterceptor(StanzaListener stanzaInterceptor); void removeStanzaInterceptor(StanzaListener stanzaInterceptor);
/**
* Registers a stanza interceptor with this connection. The interceptor will be
* invoked every time a stanza is about to be sent by this connection. Interceptors
* may modify the stanza to be sent. A stanza filter determines which stanzas
* will be delivered to the interceptor.
*
* <p>
* NOTE: For a similar functionality on incoming stanzas, see {@link #addAsyncStanzaListener(StanzaListener, StanzaFilter)}.
* </p>
*
* @param messageInterceptor the stanza interceptor to notify of stanzas about to be sent.
* @param messageFilter the stanza filter to use.
*/
void addMessageInterceptor(Consumer<MessageBuilder> messageInterceptor, Predicate<Message> messageFilter);
/**
* Removes a message interceptor.
*
* @param messageInterceptor the message interceptor to remove.
*/
void removeMessageInterceptor(Consumer<MessageBuilder> messageInterceptor);
/**
* Registers a stanza interceptor with this connection. The interceptor will be
* invoked every time a stanza is about to be sent by this connection. Interceptors
* may modify the stanza to be sent. A stanza filter determines which stanzas
* will be delivered to the interceptor.
*
* <p>
* NOTE: For a similar functionality on incoming stanzas, see {@link #addAsyncStanzaListener(StanzaListener, StanzaFilter)}.
* </p>
*
* @param presenceInterceptor the stanza interceptor to notify of stanzas about to be sent.
* @param presenceFilter the stanza filter to use.
*/
void addPresenceInterceptor(Consumer<PresenceBuilder> presenceInterceptor, Predicate<Presence> presenceFilter);
/**
* Removes a presence interceptor.
*
* @param presenceInterceptor the stanza interceptor to remove.
*/
void removePresenceInterceptor(Consumer<PresenceBuilder> presenceInterceptor);
/** /**
* Returns the current value of the reply timeout in milliseconds for request for this * Returns the current value of the reply timeout in milliseconds for request for this
* XMPPConnection instance. * XMPPConnection instance.

View file

@ -19,7 +19,6 @@ package org.jivesoftware.smack.compress.provider;
import org.jivesoftware.smack.compress.packet.Compressed; import org.jivesoftware.smack.compress.packet.Compressed;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.NonzaProvider; import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class CompressedProvider extends NonzaProvider<Compressed> { public final class CompressedProvider extends NonzaProvider<Compressed> {

View file

@ -26,7 +26,6 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.NonzaProvider; import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
@ -62,8 +61,7 @@ public final class FailureProvider extends NonzaProvider<Failure> {
case StreamOpen.SERVER_NAMESPACE: case StreamOpen.SERVER_NAMESPACE:
switch (name) { switch (name) {
case StanzaError.ERROR: case StanzaError.ERROR:
StanzaError.Builder stanzaErrorBuilder = PacketParserUtils.parseError(parser, failureXmlEnvironment); stanzaError = PacketParserUtils.parseError(parser, failureXmlEnvironment);
stanzaError = stanzaErrorBuilder.build();
break; break;
default: default:
LOGGER.warning("Unknown element in " + namespace + ": " + name); LOGGER.warning("Unknown element in " + namespace + ": " + name);

View file

@ -76,7 +76,7 @@ public class Java7ZlibInputOutputStream extends XMPPInputOutputStream {
* byte without blocking, 0 means that the system is known to block for more input. * byte without blocking, 0 means that the system is known to block for more input.
* *
* @return 0 if no data is available, 1 otherwise * @return 0 if no data is available, 1 otherwise
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
@Override @Override
public int available() throws IOException { public int available() throws IOException {

View file

@ -18,6 +18,7 @@
package org.jivesoftware.smack.filter; package org.jivesoftware.smack.filter;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.Predicate;
/** /**
* Defines a way to filter stanzas for particular attributes. Stanza filters are used when * Defines a way to filter stanzas for particular attributes. Stanza filters are used when
@ -51,7 +52,7 @@ import org.jivesoftware.smack.packet.Stanza;
* @see org.jivesoftware.smack.StanzaListener * @see org.jivesoftware.smack.StanzaListener
* @author Matt Tucker * @author Matt Tucker
*/ */
public interface StanzaFilter { public interface StanzaFilter extends Predicate<Stanza> {
/** /**
* Tests whether or not the specified stanza should pass the filter. * Tests whether or not the specified stanza should pass the filter.
@ -60,4 +61,9 @@ public interface StanzaFilter {
* @return true if and only if <code>stanza</code> passes the filter. * @return true if and only if <code>stanza</code> passes the filter.
*/ */
boolean accept(Stanza stanza); boolean accept(Stanza stanza);
@Override
default boolean test(Stanza stanza) {
return accept(stanza);
}
} }

View file

@ -0,0 +1,69 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.ToStringUtil;
public abstract class AbstractIqBuilder<IB extends AbstractIqBuilder<IB>> extends StanzaBuilder<IB> implements IqView {
protected IQ.Type type = IQ.Type.get;
AbstractIqBuilder(AbstractIqBuilder<?> other) {
super(other);
type = other.type;
}
AbstractIqBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}
AbstractIqBuilder(String stanzaId) {
super(stanzaId);
}
public static IqData createResponse(IqView request) {
return createResponse(request, IQ.ResponseType.result);
}
public static IqData createErrorResponse(IqView request) {
return createResponse(request, IQ.ResponseType.error);
}
protected static IqData createResponse(IqView request, IQ.ResponseType responseType) {
if (!(request.getType() == IQ.Type.get || request.getType() == IQ.Type.set)) {
throw new IllegalArgumentException("IQ request must be of type 'set' or 'get'. Original IQ: " + request);
}
IqData commonResponseIqData = buildResponse(request, s -> {
return StanzaBuilder.buildIqData(s);
});
commonResponseIqData.ofType(responseType.getType());
return commonResponseIqData;
}
@Override
protected final void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
builder.addValue("type", getType());
}
@Override
public final IQ.Type getType() {
return type;
}
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014 Florian Schmaus * Copyright © 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,14 +18,18 @@ package org.jivesoftware.smack.packet;
public class EmptyResultIQ extends IQ { public class EmptyResultIQ extends IQ {
EmptyResultIQ(IqData iqBuilder) {
super(iqBuilder, null, null);
}
// TODO: Deprecate when stanza builder and parsing logic is ready.
public EmptyResultIQ() { public EmptyResultIQ() {
super(null, null); super(null, null);
setType(IQ.Type.result); setType(IQ.Type.result);
} }
public EmptyResultIQ(IQ request) { public EmptyResultIQ(IQ request) {
this(); this(AbstractIqBuilder.createResponse(request));
initializeAsResultFor(request);
} }
@Override @Override

View file

@ -27,13 +27,13 @@ public class ErrorIQ extends SimpleIQ {
* <p> * <p>
* According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an &lt;error/&gt; child element.", so the xmppError argument is mandatory. * According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an &lt;error/&gt; child element.", so the xmppError argument is mandatory.
* </p> * </p>
* @param xmppErrorBuilder the XMPPError builder (required). * @param stanzaError the stanzaError (required).
*/ */
public ErrorIQ(StanzaError.Builder xmppErrorBuilder) { public ErrorIQ(StanzaError stanzaError) {
super(ELEMENT, null); super(ELEMENT, null);
Objects.requireNonNull(xmppErrorBuilder, "xmppErrorBuilder must not be null"); Objects.requireNonNull(stanzaError, "stanzaError must not be null");
setType(IQ.Type.error); setType(IQ.Type.error);
setError(xmppErrorBuilder); setError(stanzaError);
} }
} }

View file

@ -18,7 +18,7 @@ package org.jivesoftware.smack.packet;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
public interface FullyQualifiedElement extends NamedElement { public interface FullyQualifiedElement extends NamedElement, XmlLangElement {
/** /**
* Returns the root element XML namespace. * Returns the root element XML namespace.
@ -33,11 +33,7 @@ public interface FullyQualifiedElement extends NamedElement {
return new QName(namespaceURI, localPart); return new QName(namespaceURI, localPart);
} }
/** @Override
* Returns the xml:lang of this XML element, or null if one has not been set.
*
* @return the xml:lang of this XML element, or null.
*/
default String getLanguage() { default String getLanguage() {
return null; return null;
} }

View file

@ -42,7 +42,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public abstract class IQ extends Stanza { public abstract class IQ extends Stanza implements IqView {
// Don't name this field 'ELEMENT'. When it comes to IQ, ELEMENT is the child element! // Don't name this field 'ELEMENT'. When it comes to IQ, ELEMENT is the child element!
public static final String IQ_ELEMENT = "iq"; public static final String IQ_ELEMENT = "iq";
@ -54,7 +54,7 @@ public abstract class IQ extends Stanza {
private Type type = Type.get; private Type type = Type.get;
public IQ(IQ iq) { protected IQ(IQ iq) {
super(iq); super(iq);
type = iq.getType(); type = iq.getType();
this.childElementName = iq.childElementName; this.childElementName = iq.childElementName;
@ -62,7 +62,16 @@ public abstract class IQ extends Stanza {
this.childElementQName = iq.childElementQName; this.childElementQName = iq.childElementQName;
} }
// TODO: Deprecate when stanza builder is ready.
protected IQ(String childElementName, String childElementNamespace) { protected IQ(String childElementName, String childElementNamespace) {
this(IqData.EMPTY, childElementName, childElementNamespace);
}
protected IQ(AbstractIqBuilder<?> iqBuilder, String childElementName, String childElementNamespace) {
super(iqBuilder);
type = iqBuilder.type;
this.childElementName = childElementName; this.childElementName = childElementName;
this.childElementNamespace = childElementNamespace; this.childElementNamespace = childElementNamespace;
if (childElementName == null) { if (childElementName == null) {
@ -72,11 +81,7 @@ public abstract class IQ extends Stanza {
} }
} }
/** @Override
* Returns the type of the IQ packet.
*
* @return the type of the IQ packet.
*/
public Type getType() { public Type getType() {
return type; return type;
} }
@ -90,6 +95,7 @@ public abstract class IQ extends Stanza {
* *
* @param type the type of the IQ packet. * @param type the type of the IQ packet.
*/ */
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setType(Type type) { public void setType(Type type) {
this.type = Objects.requireNonNull(type, "type must not be null"); this.type = Objects.requireNonNull(type, "type must not be null");
} }
@ -260,19 +266,6 @@ public abstract class IQ extends Stanza {
*/ */
protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml); protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);
protected final void initializeAsResultFor(IQ request) {
assert this != request;
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
setType(Type.result);
}
/** /**
* Convenience method to create a new empty {@link Type#result IQ.Type.result} * Convenience method to create a new empty {@link Type#result IQ.Type.result}
* IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} * IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
@ -311,7 +304,7 @@ public abstract class IQ extends Stanza {
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. * {@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. * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
*/ */
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) { public static ErrorIQ createErrorResponse(final IQ request, final StanzaError error) {
if (!request.isRequestIQ()) { if (!request.isRequestIQ()) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
@ -321,35 +314,25 @@ public abstract class IQ extends Stanza {
result.setFrom(request.getTo()); result.setFrom(request.getTo());
result.setTo(request.getFrom()); result.setTo(request.getFrom());
error.setStanza(result);
return result; return result;
} }
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Condition condition) { /**
return createErrorResponse(request, StanzaError.getBuilder(condition)); * Deprecated.
*
* @param request the request.
* @param error the error.
* @return an error IQ.
* @deprecated use {@link #createErrorResponse(IQ, StanzaError)} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) {
return createErrorResponse(request, error.build());
} }
/** public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Condition condition) {
* Convenience method to create a new {@link Type#error IQ.Type.error} IQ return createErrorResponse(request, StanzaError.getBuilder(condition).build());
* based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
* IQ. The new stanza 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 StanzaError 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 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.
*/
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError error) {
return createErrorResponse(request, StanzaError.getBuilder(error));
} }
/** /**
@ -392,6 +375,25 @@ public abstract class IQ extends Stanza {
} }
} }
public enum ResponseType {
result(Type.result),
error(Type.error),
;
final Type type;
ResponseType(Type type) {
this.type = type;
}
Type getType() {
return type;
}
}
public static class IQChildElementXmlStringBuilder extends XmlStringBuilder { public static class IQChildElementXmlStringBuilder extends XmlStringBuilder {
private final String element; private final String element;

View file

@ -0,0 +1,44 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.util.Objects;
public abstract class IqBuilder<IB extends IqBuilder<IB, I>, I extends IQ>
extends AbstractIqBuilder<IB> {
protected IqBuilder(AbstractIqBuilder<?> other) {
super(other);
}
protected IqBuilder(XMPPConnection connection) {
super(connection.getStanzaFactory().getStanzaIdSource());
}
protected IqBuilder(String stanzaId) {
super(stanzaId);
}
public IB ofType(IQ.Type type) {
this.type = Objects.requireNonNull(type);
return getThis();
}
public abstract I build();
}

View file

@ -0,0 +1,44 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.Objects;
public final class IqData extends AbstractIqBuilder<IqData> {
static final IqData EMPTY = new IqData(StandardStanzaIdSource.DEFAULT);
IqData(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}
IqData(String stanzaId) {
super(stanzaId);
}
public IqData ofType(IQ.Type type) {
this.type = Objects.requireNonNull(type);
return getThis();
}
@Override
public IqData getThis() {
return this;
}
}

View file

@ -0,0 +1,28 @@
/**
*
* Copyright 2019 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;
public interface IqView extends StanzaView {
/**
* Returns the type of the IQ packet.
*
* @return the type of the IQ packet.
*/
IQ.Type getType();
}

View file

@ -24,6 +24,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
@ -58,7 +60,8 @@ import org.jxmpp.stringprep.XmppStringprepException;
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public final class Message extends Stanza implements TypedCloneable<Message> { public final class Message extends MessageOrPresence<MessageBuilder>
implements MessageView, TypedCloneable<Message> {
public static final String ELEMENT = "message"; public static final String ELEMENT = "message";
public static final String BODY = "body"; public static final String BODY = "body";
@ -66,11 +69,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
private Type type; private Type type;
private String thread = null; private String thread = null;
private final Set<Subject> subjects = new HashSet<Subject>();
/** /**
* Creates a new, "normal" message. * Creates a new, "normal" message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message() { public Message() {
} }
@ -78,7 +82,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Creates a new "normal" message to the specified recipient. * Creates a new "normal" message to the specified recipient.
* *
* @param to the recipient of the message. * @param to the recipient of the message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to) { public Message(Jid to) {
setTo(to); setTo(to);
} }
@ -88,7 +95,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* *
* @param to the user to send the message to. * @param to the user to send the message to.
* @param type the message type. * @param type the message type.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, Type type) { public Message(Jid to, Type type) {
this(to); this(to);
setType(type); setType(type);
@ -99,7 +109,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* *
* @param to the user to send the message to. * @param to the user to send the message to.
* @param body the body of the message. * @param body the body of the message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, String body) { public Message(Jid to, String body) {
this(to); this(to);
setBody(body); setBody(body);
@ -111,7 +124,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param to the user to send the message to. * @param to the user to send the message to.
* @param body the body of the message. * @param body the body of the message.
* @throws XmppStringprepException if 'to' is not a valid XMPP address. * @throws XmppStringprepException if 'to' is not a valid XMPP address.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(String to, String body) throws XmppStringprepException { public Message(String to, String body) throws XmppStringprepException {
this(JidCreate.from(to), body); this(JidCreate.from(to), body);
} }
@ -122,12 +138,21 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param to TODO javadoc me please * @param to TODO javadoc me please
* @param extensionElement TODO javadoc me please * @param extensionElement TODO javadoc me please
* @since 4.2 * @since 4.2
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, ExtensionElement extensionElement) { public Message(Jid to, ExtensionElement extensionElement) {
this(to); this(to);
addExtension(extensionElement); addExtension(extensionElement);
} }
Message(MessageBuilder messageBuilder) {
super(messageBuilder);
type = messageBuilder.type;
thread = messageBuilder.thread;
}
/** /**
* Copy constructor. * Copy constructor.
* <p> * <p>
@ -141,15 +166,9 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
super(other); super(other);
this.type = other.type; this.type = other.type;
this.thread = other.thread; this.thread = other.thread;
this.subjects.addAll(other.subjects);
} }
/** @Override
* Returns the type of the message. If no type has been set this method will return {@link
* org.jivesoftware.smack.packet.Message.Type#normal}.
*
* @return the type of the message.
*/
public Type getType() { public Type getType() {
if (type == null) { if (type == null) {
return Type.normal; return Type.normal;
@ -161,7 +180,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Sets the type of the message. * Sets the type of the message.
* *
* @param type the type of the message. * @param type the type of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setType(Type type) { public void setType(Type type) {
this.type = type; this.type = type;
} }
@ -195,8 +217,9 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
private Subject getMessageSubject(String language) { private Subject getMessageSubject(String language) {
language = determineLanguage(language); language = determineLanguage(language);
for (Subject subject : subjects) { for (Subject subject : getSubjects()) {
if (Objects.equals(language, subject.language)) { if (Objects.equals(language, subject.language)
|| (subject.language == null && Objects.equals(this.language, language))) {
return subject; return subject;
} }
} }
@ -210,7 +233,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return a collection of all subjects in this message. * @return a collection of all subjects in this message.
*/ */
public Set<Subject> getSubjects() { public Set<Subject> getSubjects() {
return Collections.unmodifiableSet(subjects); List<Subject> subjectList = getExtensions(Subject.class);
Set<Subject> subjects = new HashSet<>(subjectList.size());
subjects.addAll(subjectList);
return subjects;
} }
/** /**
@ -218,7 +246,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* message contents. * message contents.
* *
* @param subject the subject of the message. * @param subject the subject of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setSubject(String subject) { public void setSubject(String subject) {
if (subject == null) { if (subject == null) {
removeSubject(""); // use empty string because #removeSubject(null) is ambiguous removeSubject(""); // use empty string because #removeSubject(null) is ambiguous
@ -235,10 +266,20 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return the new {@link org.jivesoftware.smack.packet.Message.Subject} * @return the new {@link org.jivesoftware.smack.packet.Message.Subject}
* @throws NullPointerException if the subject is null, a null pointer exception is thrown * @throws NullPointerException if the subject is null, a null pointer exception is thrown
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public Subject addSubject(String language, String subject) { public Subject addSubject(String language, String subject) {
language = determineLanguage(language); language = determineLanguage(language);
List<Subject> currentSubjects = getExtensions(Subject.class);
for (Subject currentSubject : currentSubjects) {
if (language.equals(currentSubject.getLanguage())) {
throw new IllegalArgumentException("Subject with the language " + language + " already exists");
}
}
Subject messageSubject = new Subject(language, subject); Subject messageSubject = new Subject(language, subject);
subjects.add(messageSubject); addExtension(messageSubject);
return messageSubject; return messageSubject;
} }
@ -248,11 +289,13 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param language the language of the subject which is to be removed * @param language the language of the subject which is to be removed
* @return true if a subject was removed and false if it was not. * @return true if a subject was removed and false if it was not.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeSubject(String language) { public boolean removeSubject(String language) {
language = determineLanguage(language); language = determineLanguage(language);
for (Subject subject : subjects) { for (Subject subject : getExtensions(Subject.class)) {
if (language.equals(subject.language)) { if (language.equals(subject.language)) {
return subjects.remove(subject); return removeSubject(subject);
} }
} }
return false; return false;
@ -264,8 +307,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param subject the subject being removed from the message. * @param subject the subject being removed from the message.
* @return true if the subject was successfully removed and false if it was not. * @return true if the subject was successfully removed and false if it was not.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeSubject(Subject subject) { public boolean removeSubject(Subject subject) {
return subjects.remove(subject); return removeExtension(subject) != null;
} }
/** /**
@ -276,7 +321,7 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
public List<String> getSubjectLanguages() { public List<String> getSubjectLanguages() {
Subject defaultSubject = getMessageSubject(null); Subject defaultSubject = getMessageSubject(null);
List<String> languages = new ArrayList<String>(); List<String> languages = new ArrayList<String>();
for (Subject subject : subjects) { for (Subject subject : getExtensions(Subject.class)) {
if (!subject.equals(defaultSubject)) { if (!subject.equals(defaultSubject)) {
languages.add(subject.language); languages.add(subject.language);
} }
@ -345,7 +390,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param body the body of the message. * @param body the body of the message.
* @see #setBody(String) * @see #setBody(String)
* @since 4.2 * @since 4.2
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setBody(CharSequence body) { public void setBody(CharSequence body) {
String bodyString; String bodyString;
if (body != null) { if (body != null) {
@ -360,7 +408,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Sets the body of the message. The body is the main message contents. * Sets the body of the message. The body is the main message contents.
* *
* @param body the body of the message. * @param body the body of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setBody(String body) { public void setBody(String body) {
if (body == null) { if (body == null) {
removeBody(""); // use empty string because #removeBody(null) is ambiguous removeBody(""); // use empty string because #removeBody(null) is ambiguous
@ -377,7 +428,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return the new {@link org.jivesoftware.smack.packet.Message.Body} * @return the new {@link org.jivesoftware.smack.packet.Message.Body}
* @throws NullPointerException if the body is null, a null pointer exception is thrown * @throws NullPointerException if the body is null, a null pointer exception is thrown
* @since 3.0.2 * @since 3.0.2
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public Body addBody(String language, String body) { public Body addBody(String language, String body) {
language = determineLanguage(language); language = determineLanguage(language);
@ -393,7 +447,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* *
* @param language the language of the body which is to be removed * @param language the language of the body which is to be removed
* @return true if a body was removed and false if it was not. * @return true if a body was removed and false if it was not.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeBody(String language) { public boolean removeBody(String language) {
language = determineLanguage(language); language = determineLanguage(language);
for (Body body : getBodies()) { for (Body body : getBodies()) {
@ -412,7 +469,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param body the body being removed from the message. * @param body the body being removed from the message.
* @return true if the body was successfully removed and false if it was not. * @return true if the body was successfully removed and false if it was not.
* @since 3.0.2 * @since 3.0.2
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeBody(Body body) { public boolean removeBody(Body body) {
ExtensionElement removedElement = removeExtension(body); ExtensionElement removedElement = removeExtension(body);
return removedElement != null; return removedElement != null;
@ -450,7 +510,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* of "chat" messages. * of "chat" messages.
* *
* @param thread the thread id of the message. * @param thread the thread id of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setThread(String thread) { public void setThread(String thread) {
this.thread = thread; this.thread = thread;
} }
@ -472,6 +535,11 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
return ELEMENT; return ELEMENT;
} }
@Override
public MessageBuilder asBuilder() {
return StanzaBuilder.buildMessageFrom(this, getStanzaId());
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -491,18 +559,6 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
buf.optAttribute("type", type); buf.optAttribute("type", type);
buf.rightAngleBracket(); buf.rightAngleBracket();
// Add the subject in the default language
Subject defaultSubject = getMessageSubject(null);
if (defaultSubject != null) {
buf.element("subject", defaultSubject.subject);
}
// Add the subject in other languages
for (Subject subject : getSubjects()) {
// Skip the default language
if (subject.equals(defaultSubject))
continue;
buf.append(subject);
}
buf.optElement("thread", thread); buf.optElement("thread", thread);
// Append the error subpacket if the message type is an error. // Append the error subpacket if the message type is an error.
if (type == Type.error) { if (type == Type.error) {
@ -537,10 +593,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
public static final String ELEMENT = "subject"; public static final String ELEMENT = "subject";
public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE; public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE;
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final String subject; private final String subject;
private final String language; private final String language;
private Subject(String language, String subject) { public Subject(String language, String subject) {
if (subject == null) { if (subject == null) {
throw new NullPointerException("Subject cannot be null."); throw new NullPointerException("Subject cannot be null.");
} }
@ -608,6 +666,7 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
public static final String ELEMENT = "body"; public static final String ELEMENT = "body";
public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE; public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE;
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
enum BodyElementNamespace { enum BodyElementNamespace {
client(StreamOpen.CLIENT_NAMESPACE), client(StreamOpen.CLIENT_NAMESPACE),

View file

@ -0,0 +1,165 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.Message.Body;
import org.jivesoftware.smack.packet.Message.Subject;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.ToStringUtil;
public final class MessageBuilder extends MessageOrPresenceBuilder<Message, MessageBuilder> implements MessageView {
static final MessageBuilder EMPTY = new MessageBuilder(() -> {
return null;
});
Message.Type type;
String thread;
MessageBuilder(Message message, String stanzaId) {
super(message, stanzaId);
copyFromMessage(message);
}
MessageBuilder(Message message, StanzaIdSource stanzaIdSource) {
super(message, stanzaIdSource);
copyFromMessage(message);
}
MessageBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}
MessageBuilder(String stanzaId) {
super(stanzaId);
}
private void copyFromMessage(Message message) {
type = message.getType();
thread = message.getThread();
}
@Override
protected void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
builder.addValue("type", type)
.addValue("thread", thread)
;
}
public MessageBuilder ofType(Message.Type type) {
this.type = type;
return getThis();
}
public MessageBuilder setThread(String thread) {
this.thread = thread;
return getThis();
}
/**
* Sets the subject of the message. The subject is a short description of
* message contents.
*
* @param subject the subject of the message.
* @return a reference to this builder.
*/
public MessageBuilder setSubject(String subject) {
return addSubject(null, subject);
}
/**
* Adds a subject with a corresponding language.
*
* @param language the language of the subject being added.
* @param subject the subject being added to the message.
* @return a reference to this builder.
* @throws NullPointerException if the subject is null.
*/
public MessageBuilder addSubject(String language, String subject) {
language = StringUtils.requireNullOrNotEmpty(language, "language must be null or not empty");
for (Subject currentSubject : getExtensions(Subject.class)) {
if (StringUtils.nullSafeCharSequenceEquals(language, currentSubject.getLanguage())) {
throw new IllegalArgumentException("Subject with the language " + language + " already exists");
}
}
Subject messageSubject = new Subject(language, subject);
addExtension(messageSubject);
return this;
}
/**
* Sets the body of the message.
*
* @param body the body of the message.
* @return a reference to this builder.
* @see #setBody(String)
*/
public MessageBuilder setBody(CharSequence body) {
return setBody(body.toString());
}
/**
* Sets the body of the message. The body is the main message contents.
*
* @param body the body of the message.
* @return a reference to this builder.
*/
public MessageBuilder setBody(String body) {
return addBody(null, body);
}
/**
* Adds a body with a corresponding language.
*
* @param language the language of the body being added.
* @param body the body being added to the message.
* @return a reference to this builder.
*/
public MessageBuilder addBody(String language, String body) {
language = StringUtils.requireNullOrNotEmpty(language, "language must be null or not empty");
for (Body currentBody : getExtensions(Body.class)) {
if (StringUtils.nullSafeCharSequenceEquals(language, currentBody.getLanguage())) {
throw new IllegalArgumentException("Bodyt with the language " + language + " already exists");
}
}
Body messageBody = new Body(language, body);
addExtension(messageBody);
return this;
}
@Override
public MessageBuilder getThis() {
return this;
}
@Override
public Message build() {
return new Message(this);
}
@Override
public Message.Type getType() {
return type;
}
}

View file

@ -0,0 +1,36 @@
/**
*
* Copyright 2019 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;
public abstract class MessageOrPresence<MPB extends MessageOrPresenceBuilder<?, ?>> extends Stanza {
@Deprecated
// TODO: Remove in Smack 4.5.
protected MessageOrPresence() {
}
protected MessageOrPresence(StanzaBuilder<?> stanzaBuilder) {
super(stanzaBuilder);
}
protected MessageOrPresence(Stanza other) {
super(other);
}
public abstract MPB asBuilder();
}

View file

@ -0,0 +1,42 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.id.StanzaIdSource;
public abstract class MessageOrPresenceBuilder<MP extends MessageOrPresence<? extends MessageOrPresenceBuilder<MP, SB>>, SB extends StanzaBuilder<SB>>
extends StanzaBuilder<SB> {
protected MessageOrPresenceBuilder(Stanza stanza, StanzaIdSource stanzaIdSource) {
super(stanza, stanzaIdSource);
}
protected MessageOrPresenceBuilder(Stanza stanza, String stanzaId) {
super(stanza, stanzaId);
}
protected MessageOrPresenceBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}
protected MessageOrPresenceBuilder(String stanzaId) {
super(stanzaId);
}
public abstract MP build();
}

View file

@ -0,0 +1,29 @@
/**
*
* Copyright 2019 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;
public interface MessageView extends StanzaView {
/**
* Returns the type of the message. If no type has been set this method will return {@link
* org.jivesoftware.smack.packet.Message.Type#normal}.
*
* @return the type of the message.
*/
Message.Type getType();
}

View file

@ -19,7 +19,8 @@ package org.jivesoftware.smack.packet;
import java.util.Locale; import java.util.Locale;
import org.jivesoftware.smack.packet.id.StanzaIdUtil; import javax.net.SocketFactory;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.TypedCloneable;
@ -60,7 +61,8 @@ import org.jxmpp.jid.Jid;
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public final class Presence extends Stanza implements TypedCloneable<Presence> { public final class Presence extends MessageOrPresence<PresenceBuilder>
implements PresenceView, TypedCloneable<Presence> {
public static final String ELEMENT = "presence"; public static final String ELEMENT = "presence";
@ -81,7 +83,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* Creates a new presence update. Status, priority, and mode are left un-set. * Creates a new presence update. Status, priority, and mode are left un-set.
* *
* @param type the type. * @param type the type.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Type type) { public Presence(Type type) {
// Ensure that the stanza ID is set by calling super(). // Ensure that the stanza ID is set by calling super().
super(); super();
@ -94,7 +99,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param to the recipient. * @param to the recipient.
* @param type the type. * @param type the type.
* @since 4.2 * @since 4.2
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Jid to, Type type) { public Presence(Jid to, Type type) {
this(type); this(type);
setTo(to); setTo(to);
@ -107,7 +115,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param status a text message describing the presence update. * @param status a text message describing the presence update.
* @param priority the priority of this presence update. * @param priority the priority of this presence update.
* @param mode the mode type for this presence update. * @param mode the mode type for this presence update.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Type type, String status, int priority, Mode mode) { public Presence(Type type, String status, int priority, Mode mode) {
// Ensure that the stanza ID is set by calling super(). // Ensure that the stanza ID is set by calling super().
super(); super();
@ -117,6 +128,14 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
setMode(mode); setMode(mode);
} }
Presence(PresenceBuilder presenceBuilder) {
super(presenceBuilder);
type = presenceBuilder.type;
status = presenceBuilder.status;
priority = presenceBuilder.priority;
mode = presenceBuilder.mode;
}
/** /**
* Copy constructor. * Copy constructor.
* <p> * <p>
@ -163,11 +182,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd); return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd);
} }
/** @Override
* Returns the type of this presence packet.
*
* @return the type of the presence packet.
*/
public Type getType() { public Type getType() {
return type; return type;
} }
@ -176,18 +191,15 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* Sets the type of the presence packet. * Sets the type of the presence packet.
* *
* @param type the type of the presence packet. * @param type the type of the presence packet.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setType(Type type) { public void setType(Type type) {
this.type = Objects.requireNonNull(type, "Type cannot be null"); this.type = Objects.requireNonNull(type, "Type cannot be null");
} }
/** @Override
* Returns the status message of the presence update, or <code>null</code> if there
* is not a status. The status is free-form text describing a user's presence
* (i.e., "gone to lunch").
*
* @return the status message.
*/
public String getStatus() { public String getStatus() {
return status; return status;
} }
@ -197,18 +209,21 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* describing a user's presence (i.e., "gone to lunch"). * describing a user's presence (i.e., "gone to lunch").
* *
* @param status the status message. * @param status the status message.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setStatus(String status) { public void setStatus(String status) {
this.status = status; this.status = status;
} }
/** @Override
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
public int getPriority() { public int getPriority() {
return getPriorityByte();
}
@Override
public byte getPriorityByte() {
if (priority == null) { if (priority == null) {
return 0; return 0;
} }
@ -221,7 +236,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param priority the priority of the presence. * @param priority the priority of the presence.
* @throws IllegalArgumentException if the priority is outside the valid range. * @throws IllegalArgumentException if the priority is outside the valid range.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a> * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setPriority(int priority) { public void setPriority(int priority) {
if (priority < -128 || priority > 127) { if (priority < -128 || priority > 127) {
throw new IllegalArgumentException("Priority value " + priority + throw new IllegalArgumentException("Priority value " + priority +
@ -234,11 +252,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
this.priority = priority; this.priority = priority;
} }
/** @Override
* Returns the mode of the presence update.
*
* @return the mode.
*/
public Mode getMode() { public Mode getMode() {
if (mode == null) { if (mode == null) {
return Mode.available; return Mode.available;
@ -251,7 +265,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* to be the same thing as {@link Presence.Mode#available}. * to be the same thing as {@link Presence.Mode#available}.
* *
* @param mode the mode. * @param mode the mode.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setMode(Mode mode) { public void setMode(Mode mode) {
this.mode = mode; this.mode = mode;
} }
@ -261,6 +278,11 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
return ELEMENT; return ELEMENT;
} }
@Override
public PresenceBuilder asBuilder() {
return StanzaBuilder.buildPresenceFrom(this, getStanzaId());
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -326,7 +348,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
*/ */
public Presence cloneWithNewId() { public Presence cloneWithNewId() {
Presence clone = clone(); Presence clone = clone();
clone.setStanzaId(StanzaIdUtil.newStanzaId()); clone.setNewStanzaId();
return clone; return clone;
} }

View file

@ -0,0 +1,141 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.Presence.Mode;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ToStringUtil;
public final class PresenceBuilder extends MessageOrPresenceBuilder<Presence, PresenceBuilder> implements PresenceView {
static final PresenceBuilder EMPTY = new PresenceBuilder(() -> {
return null;
});
Presence.Type type = Presence.Type.available;
String status;
Byte priority;
Presence.Mode mode;
PresenceBuilder(Presence presence, String stanzaId) {
super(presence, stanzaId);
copyFromPresence(presence);
}
PresenceBuilder(Presence presence, StanzaIdSource stanzaIdSource) {
super(presence, stanzaIdSource);
copyFromPresence(presence);
}
PresenceBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}
PresenceBuilder(String stanzaId) {
super(stanzaId);
}
private void copyFromPresence(Presence presence) {
type = presence.getType();
status = presence.getStatus();
priority = presence.getPriorityByte();
mode = presence.getMode();
}
@Override
protected void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
builder.addValue("type", type)
.addValue("mode", mode)
.addValue("priority", priority)
.addValue("status", status)
;
}
public PresenceBuilder ofType(Presence.Type type) {
this.type = Objects.requireNonNull(type, "Type cannot be null");
return getThis();
}
public PresenceBuilder setStatus(String status) {
this.status = status;
return getThis();
}
public PresenceBuilder setPriority(int priority) {
if (priority < -128 || priority > 127) {
throw new IllegalArgumentException("Priority value " + priority +
" is not valid. Valid range is -128 through 127.");
}
Byte priorityByte = (byte) priority;
return setPriority(priorityByte);
}
public PresenceBuilder setPriority(Byte priority) {
this.priority = priority;
return getThis();
}
public PresenceBuilder setMode(Presence.Mode mode) {
this.mode = mode;
return getThis();
}
@Override
public PresenceBuilder getThis() {
return this;
}
@Override
public Presence build() {
return new Presence(this);
}
@Override
public Presence.Type getType() {
return type;
}
@Override
public String getStatus() {
return status;
}
@Override
public int getPriority() {
return getPriorityByte();
}
@Override
public byte getPriorityByte() {
if (priority == null) {
return 0;
}
return priority;
}
@Override
public Presence.Mode getMode() {
if (mode == null) {
return Mode.available;
}
return mode;
}
}

View file

@ -0,0 +1,59 @@
/**
*
* Copyright 2019 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;
public interface PresenceView extends StanzaView {
/**
* Returns the type of this presence stanza.
*
* @return the type of the presence stanza.
*/
Presence.Type getType();
/**
* Returns the status message of the presence update, or <code>null</code> if there
* is not a status. The status is free-form text describing a user's presence
* (i.e., "gone to lunch").
*
* @return the status message.
*/
String getStatus();
/**
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
int getPriority();
/**
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
byte getPriorityByte();
/**
* Returns the mode of the presence update.
*
* @return the mode.
*/
Presence.Mode getMode();
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014 Florian Schmaus * Copyright © 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,10 @@ public abstract class SimpleIQ extends IQ {
super(childElementName, childElementNamespace); super(childElementName, childElementNamespace);
} }
protected SimpleIQ(IqData iqBuilder, String childElementName, String childElementNamespace) {
super(iqBuilder, childElementName, childElementNamespace);
}
@Override @Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.setEmptyElement(); xml.setEmptyElement();

View file

@ -20,15 +20,19 @@ package org.jivesoftware.smack.packet;
import static org.jivesoftware.smack.util.StringUtils.requireNotNullNorEmpty; import static org.jivesoftware.smack.util.StringUtils.requireNotNullNorEmpty;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.id.StanzaIdUtil; import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.PacketUtil; import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
@ -43,12 +47,16 @@ import org.jxmpp.jid.Jid;
* XMPP Stanzas are {@link Message}, {@link IQ} and {@link Presence}. Which therefore subclass this * XMPP Stanzas are {@link Message}, {@link IQ} and {@link Presence}. Which therefore subclass this
* class. <b>If you think you need to subclass this class, then you are doing something wrong.</b> * class. <b>If you think you need to subclass this class, then you are doing something wrong.</b>
* </p> * </p>
* <p>
* Use {@link StanzaBuilder} to construct a stanza instance. All instance mutating methods of this
* class are deprecated, although not all of them are currently marked as such, and must not be used.
* </p>
* *
* @author Matt Tucker * @author Matt Tucker
* @author Florian Schmaus * @author Florian Schmaus
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#stanzas">RFC 6120 § 8. XML Stanzas</a> * @see <a href="http://xmpp.org/rfcs/rfc6120.html#stanzas">RFC 6120 § 8. XML Stanzas</a>
*/ */
public abstract class Stanza implements TopLevelStreamElement { public abstract class Stanza implements StanzaView, 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";
@ -56,12 +64,14 @@ public abstract class Stanza implements TopLevelStreamElement {
protected static final String DEFAULT_LANGUAGE = protected static final String DEFAULT_LANGUAGE =
java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US); java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US);
private final MultiMap<QName, ExtensionElement> extensionElements = new MultiMap<>(); private final MultiMap<QName, ExtensionElement> extensionElements;
// Assume that all stanzas Smack handles are in the client namespace, since Smack is an XMPP client library. We can // Assume that all stanzas Smack handles are in the client namespace, since Smack is an XMPP client library. We can
// change this behavior later if it is required. // change this behavior later if it is required.
private final String namespace = StreamOpen.CLIENT_NAMESPACE; private final String namespace = StreamOpen.CLIENT_NAMESPACE;
private final StanzaIdSource usedStanzaIdSource;
private String id = null; private String id = null;
private Jid to; private Jid to;
private Jid from; private Jid from;
@ -80,31 +90,47 @@ public abstract class Stanza implements TopLevelStreamElement {
protected String language; protected String language;
protected Stanza() { protected Stanza() {
this(StanzaIdUtil.newStanzaId()); extensionElements = new MultiMap<>();
usedStanzaIdSource = null;
id = StandardStanzaIdSource.DEFAULT.getNewStanzaId();
} }
protected Stanza(String stanzaId) { protected Stanza(StanzaBuilder<?> stanzaBuilder) {
setStanzaId(stanzaId); if (stanzaBuilder.stanzaIdSource != null) {
id = stanzaBuilder.stanzaIdSource.getNewStanzaId();
// Note that some stanza ID sources, e.g. StanzaBuilder.PresenceBuilder.EMPTY return null here. Hence we
// only check that the returned string is not empty.
assert StringUtils.isNullOrNotEmpty(id);
usedStanzaIdSource = stanzaBuilder.stanzaIdSource;
} else {
// N.B. It is ok if stanzaId here is null.
id = stanzaBuilder.stanzaId;
usedStanzaIdSource = null;
}
to = stanzaBuilder.to;
from = stanzaBuilder.from;
error = stanzaBuilder.stanzaError;
language = stanzaBuilder.language;
extensionElements = stanzaBuilder.extensionElements.clone();
} }
protected Stanza(Stanza p) { protected Stanza(Stanza p) {
usedStanzaIdSource = p.usedStanzaIdSource;
id = p.getStanzaId(); id = p.getStanzaId();
to = p.getTo(); to = p.getTo();
from = p.getFrom(); from = p.getFrom();
error = p.error; error = p.error;
// Copy extensions extensionElements = p.extensionElements.clone();
for (ExtensionElement pe : p.getExtensions()) {
addExtension(pe);
}
} }
/** @Override
* Returns the unique ID of the stanza. The returned value could be <code>null</code>. public final String getStanzaId() {
*
* @return the packet's unique ID or <code>null</code> if the id is not available.
*/
public String getStanzaId() {
return id; return id;
} }
@ -127,7 +153,7 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return true if the stanza ID is set, false otherwise. * @return true if the stanza ID is set, false otherwise.
* @since 4.1 * @since 4.1
*/ */
public boolean hasStanzaIdSet() { public final boolean hasStanzaIdSet() {
// setStanzaId ensures that the id is either null or not empty, // setStanzaId ensures that the id is either null or not empty,
// so we can assume that it is set if it's not null. // so we can assume that it is set if it's not null.
return id != null; return id != null;
@ -138,57 +164,51 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @return the stanza id. * @return the stanza id.
* @since 4.2 * @since 4.2
* @deprecated use {@link #setNewStanzaId()} instead. * @deprecated use {@link StanzaBuilder} instead.
*/ */
@Deprecated @Deprecated
// TODO: Remove in Smack 4.5. // TODO: Remove in Smack 4.5.
public String setStanzaId() { public String setStanzaId() {
return ensureStanzaIdSet(); if (!hasStanzaIdSet()) {
} setNewStanzaId();
/**
* Set a new stanza ID even if there is already one set.
*
* @return the stanza id.
* @since 4.4
*/
public String setNewStanzaId() {
return ensureStanzaIdSet(true);
}
/**
* Ensure a stanza id is set.
*
* @return the stanza id.
* @since 4.4
*/
public String ensureStanzaIdSet() {
return ensureStanzaIdSet(false);
}
/**
* Ensure that a stanza ID is set.
*
* @param forceNew force a new ID even if there is already one set.
* @return the stanza ID.
* @since 4.4
*/
private String ensureStanzaIdSet(boolean forceNew) {
if (forceNew || !hasStanzaIdSet()) {
setStanzaId(StanzaIdUtil.newStanzaId());
} }
return getStanzaId(); return getStanzaId();
} }
/** /**
* Returns who the stanza is being sent "to", or <code>null</code> if * Throws an {@link IllegalArgumentException} if this stanza has no stanza ID set.
* the value is not set. The XMPP protocol often makes the "to"
* attribute optional, so it does not always need to be set.<p>
* *
* @return who the stanza is being sent to, or <code>null</code> if the * @throws IllegalArgumentException if this stanza has no stanza ID set.
* value has not been set. * @since 4.4.
*/ */
public Jid getTo() { public final void throwIfNoStanzaId() {
if (hasStanzaIdSet()) {
return;
}
throw new IllegalArgumentException("The stanza has no RFC stanza ID set, although one is required");
}
/**
* Ensure that a stanza ID is set.
*
* @return the stanza ID.
* @since 4.4
*/
// TODO: Remove this method once StanzaBuilder is ready.
protected String setNewStanzaId() {
if (usedStanzaIdSource != null) {
id = usedStanzaIdSource.getNewStanzaId();
}
else {
id = StandardStanzaIdSource.DEFAULT.getNewStanzaId();
}
return getStanzaId();
}
@Override
public final Jid getTo() {
return to; return to;
} }
@ -198,19 +218,13 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @param to who the packet is being sent to. * @param to who the packet is being sent to.
*/ */
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setTo(Jid to) { public void setTo(Jid to) {
this.to = to; this.to = to;
} }
/** @Override
* Returns who the stanza is being sent "from" or <code>null</code> if public final Jid getFrom() {
* the value is not set. The XMPP protocol often makes the "from"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the stanza is being sent from, or <code>null</code> if the
* value has not been set.
*/
public Jid getFrom() {
return from; return from;
} }
@ -221,35 +235,39 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @param from who the packet is being sent to. * @param from who the packet is being sent to.
*/ */
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setFrom(Jid from) { public void setFrom(Jid from) {
this.from = from; this.from = from;
} }
/** @Override
* Returns the error associated with this packet, or <code>null</code> if there are public final StanzaError getError() {
* no errors.
*
* @return the error sub-packet or <code>null</code> if there isn't an error.
*/
public StanzaError getError() {
return error; return error;
} }
/** /**
* Sets the error for this stanza. * Sets the error for this stanza.
* *
* @param xmppErrorBuilder the error to associate with this stanza. * @param stanzaError the error that this stanza carries and hence signals.
*/ */
public void setError(StanzaError.Builder xmppErrorBuilder) { // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
if (xmppErrorBuilder == null) { public void setError(StanzaError stanzaError) {
return; error = stanzaError;
} }
xmppErrorBuilder.setStanza(this);
error = xmppErrorBuilder.build(); /**
* Deprecated.
* @param stanzaError the stanza error.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setError(StanzaError.Builder stanzaError) {
setError(stanzaError.build());
} }
@Override @Override
public String getLanguage() { public final String getLanguage() {
return language; return language;
} }
@ -257,23 +275,32 @@ public abstract class Stanza implements TopLevelStreamElement {
* Sets the xml:lang of this Stanza. * Sets the xml:lang of this Stanza.
* *
* @param language the xml:lang of this Stanza. * @param language the xml:lang of this Stanza.
* @deprecated use {@link StanzaBuilder#setLanguage(String)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void setLanguage(String language) { public void setLanguage(String language) {
this.language = language; this.language = language;
} }
/** @Override
* Returns a list of all extension elements of this stanza. public final List<ExtensionElement> getExtensions() {
*
* @return a list of all extension elements of this stanza.
*/
public List<ExtensionElement> getExtensions() {
synchronized (extensionElements) { synchronized (extensionElements) {
// No need to create a new list, values() will already create a new one for us // No need to create a new list, values() will already create a new one for us
return extensionElements.values(); return extensionElements.values();
} }
} }
public final MultiMap<QName, ExtensionElement> getExtensionsMap() {
return cloneExtensionsMap();
}
final MultiMap<QName, ExtensionElement> cloneExtensionsMap() {
synchronized (extensionElements) {
return extensionElements.clone();
}
}
/** /**
* Return a list of all extensions with the given element name <em>and</em> namespace. * Return a list of all extensions with the given element name <em>and</em> namespace.
* <p> * <p>
@ -285,11 +312,27 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return a set of all matching extensions. * @return a set of all matching extensions.
* @since 4.1 * @since 4.1
*/ */
public List<ExtensionElement> getExtensions(String elementName, String namespace) { public final List<ExtensionElement> getExtensions(String elementName, String namespace) {
requireNotNullNorEmpty(elementName, "elementName must not be null nor empty"); requireNotNullNorEmpty(elementName, "elementName must not be null nor empty");
requireNotNullNorEmpty(namespace, "namespace must not be null nor empty"); requireNotNullNorEmpty(namespace, "namespace must not be null nor empty");
QName key = new QName(namespace, elementName); QName key = new QName(namespace, elementName);
return extensionElements.getAll(key); return getExtensions(key);
}
@Override
public final List<ExtensionElement> getExtensions(QName qname) {
List<ExtensionElement> res;
synchronized (extensionElements) {
res = extensionElements.getAll(qname);
}
return Collections.unmodifiableList(res);
}
@Override
public final <E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass) {
synchronized (extensionElements) {
return XmppElementUtil.getElementsFrom(extensionElements, extensionElementClass);
}
} }
/** /**
@ -301,7 +344,7 @@ public abstract class Stanza implements TopLevelStreamElement {
* @param namespace the namespace of the extension that is desired. * @param namespace the namespace of the extension that is desired.
* @return the stanza extension with the given namespace. * @return the stanza extension with the given namespace.
*/ */
public ExtensionElement getExtension(String namespace) { public final ExtensionElement getExtension(String namespace) {
return PacketUtil.extensionElementFrom(getExtensions(), null, namespace); return PacketUtil.extensionElementFrom(getExtensions(), null, namespace);
} }
@ -317,27 +360,37 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return the extension, or <code>null</code> if it doesn't exist. * @return the extension, or <code>null</code> if it doesn't exist.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <PE extends ExtensionElement> PE getExtension(String elementName, String namespace) { public final <PE extends ExtensionElement> PE getExtension(String elementName, String namespace) {
if (namespace == null) { if (namespace == null) {
return null; return null;
} }
QName key = new QName(namespace, elementName); QName key = new QName(namespace, elementName);
ExtensionElement packetExtension; ExtensionElement packetExtension = getExtension(key);
synchronized (extensionElements) {
packetExtension = extensionElements.getFirst(key);
}
if (packetExtension == null) { if (packetExtension == null) {
return null; return null;
} }
return (PE) packetExtension; return (PE) packetExtension;
} }
@SuppressWarnings("unchecked")
@Override
public final <E extends ExtensionElement> E getExtension(QName qname) {
synchronized (extensionElements) {
return (E) extensionElements.getFirst(qname);
}
}
/** /**
* Adds a stanza extension to the packet. Does nothing if extension is null. * Adds a stanza extension to the packet. Does nothing if extension is null.
* <p>
* Please note that although this method is not yet marked as deprecated, it is recommended to use
* {@link StanzaBuilder#addExtension(ExtensionElement)} instead.
* </p>
* *
* @param extension a stanza extension. * @param extension a stanza extension.
*/ */
public void addExtension(ExtensionElement extension) { // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public final void addExtension(ExtensionElement extension) {
if (extension == null) return; if (extension == null) return;
QName key = extension.getQName(); QName key = extension.getQName();
synchronized (extensionElements) { synchronized (extensionElements) {
@ -348,12 +401,17 @@ public abstract class Stanza implements TopLevelStreamElement {
/** /**
* Add the given extension and override eventually existing extensions with the same name and * Add the given extension and override eventually existing extensions with the same name and
* namespace. * namespace.
* <p>
* Please note that although this method is not yet marked as deprecated, it is recommended to use
* {@link StanzaBuilder#overrideExtension(ExtensionElement)} instead.
* </p>
* *
* @param extension the extension element to add. * @param extension the extension element to add.
* @return one of the removed extensions or <code>null</code> if there are none. * @return one of the removed extensions or <code>null</code> if there are none.
* @since 4.1.2 * @since 4.1.2
*/ */
public ExtensionElement overrideExtension(ExtensionElement extension) { // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public final ExtensionElement overrideExtension(ExtensionElement extension) {
if (extension == null) return null; if (extension == null) return null;
synchronized (extensionElements) { synchronized (extensionElements) {
// Note that we need to use removeExtension(String, String) here. If would use // Note that we need to use removeExtension(String, String) here. If would use
@ -370,7 +428,8 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @param extensions a collection of stanza extensions * @param extensions a collection of stanza extensions
*/ */
public void addExtensions(Collection<ExtensionElement> extensions) { // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public final void addExtensions(Collection<ExtensionElement> extensions) {
if (extensions == null) return; if (extensions == null) return;
for (ExtensionElement packetExtension : extensions) { for (ExtensionElement packetExtension : extensions) {
addExtension(packetExtension); addExtension(packetExtension);
@ -387,7 +446,7 @@ public abstract class Stanza implements TopLevelStreamElement {
* @param namespace TODO javadoc me please * @param namespace TODO javadoc me please
* @return true if a stanza extension exists, false otherwise. * @return true if a stanza extension exists, false otherwise.
*/ */
public boolean hasExtension(String elementName, String namespace) { public final boolean hasExtension(String elementName, String namespace) {
if (elementName == null) { if (elementName == null) {
return hasExtension(namespace); return hasExtension(namespace);
} }
@ -397,13 +456,9 @@ public abstract class Stanza implements TopLevelStreamElement {
} }
} }
/** // Overridden in order to avoid an extra copy.
* Check if a stanza extension with the given namespace exists. @Override
* public final boolean hasExtension(String namespace) {
* @param namespace TODO javadoc me please
* @return true if a stanza extension exists, false otherwise.
*/
public boolean hasExtension(String namespace) {
synchronized (extensionElements) { synchronized (extensionElements) {
for (ExtensionElement packetExtension : extensionElements.values()) { for (ExtensionElement packetExtension : extensionElements.values()) {
if (packetExtension.getNamespace().equals(namespace)) { if (packetExtension.getNamespace().equals(namespace)) {
@ -421,7 +476,8 @@ public abstract class Stanza implements TopLevelStreamElement {
* @param namespace TODO javadoc me please * @param namespace TODO javadoc me please
* @return the removed stanza extension or null. * @return the removed stanza extension or null.
*/ */
public ExtensionElement removeExtension(String elementName, String namespace) { // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public final ExtensionElement removeExtension(String elementName, String namespace) {
QName key = new QName(namespace, elementName); QName key = new QName(namespace, elementName);
synchronized (extensionElements) { synchronized (extensionElements) {
return extensionElements.remove(key); return extensionElements.remove(key);
@ -433,8 +489,11 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @param extension the stanza extension to remove. * @param extension the stanza extension to remove.
* @return the removed stanza extension or null. * @return the removed stanza extension or null.
* @deprecated use {@link StanzaBuilder} instead.
*/ */
public ExtensionElement removeExtension(ExtensionElement extension) { @Deprecated
// TODO: Remove in Smack 4.5.
public final ExtensionElement removeExtension(ExtensionElement extension) {
QName key = extension.getQName(); QName key = extension.getQName();
synchronized (extensionElements) { synchronized (extensionElements) {
List<ExtensionElement> list = extensionElements.getAll(key); List<ExtensionElement> list = extensionElements.getAll(key);
@ -494,7 +553,7 @@ public abstract class Stanza implements TopLevelStreamElement {
* *
* @param xml the XmlStringBuilder to append the error to. * @param xml the XmlStringBuilder to append the error to.
*/ */
protected void appendErrorIfExists(XmlStringBuilder xml) { protected final void appendErrorIfExists(XmlStringBuilder xml) {
StanzaError error = getError(); StanzaError error = getError();
if (error != null) { if (error != null) {
xml.append(error); xml.append(error);

View file

@ -0,0 +1,306 @@
/**
*
* Copyright 2019 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.Collection;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.Function;
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.ToStringUtil;
import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public abstract class StanzaBuilder<B extends StanzaBuilder<B>> implements StanzaView {
final StanzaIdSource stanzaIdSource;
final String stanzaId;
Jid to;
Jid from;
StanzaError stanzaError;
String language;
MultiMap<QName, ExtensionElement> extensionElements = new MultiMap<>();
protected StanzaBuilder(StanzaBuilder<?> other) {
stanzaIdSource = other.stanzaIdSource;
stanzaId = other.stanzaId;
to = other.to;
from = other.from;
stanzaError = other.stanzaError;
language = other.language;
extensionElements = other.extensionElements.clone();
}
protected StanzaBuilder(StanzaIdSource stanzaIdSource) {
this.stanzaIdSource = stanzaIdSource;
this.stanzaId = null;
}
protected StanzaBuilder(String stanzaId) {
this.stanzaIdSource = null;
this.stanzaId = StringUtils.requireNullOrNotEmpty(stanzaId, "Stanza ID must not be the empty String");
}
protected StanzaBuilder(Stanza message, String stanzaId) {
this(stanzaId);
copyFromStanza(message);
}
protected StanzaBuilder(Stanza message, StanzaIdSource stanzaIdSource) {
this(stanzaIdSource);
copyFromStanza(message);
}
private void copyFromStanza(Stanza stanza) {
to = stanza.getTo();
from = stanza.getFrom();
stanzaError = stanza.getError();
language = stanza.getLanguage();
extensionElements = stanza.cloneExtensionsMap();
}
/**
* Set the recipent address of the stanza.
*
* @param to whoe the stanza is being sent to.
* @return a reference to this builder.
* @throws XmppStringprepException if the provided character sequence is not a valid XMPP address.
* @see #to(Jid)
*/
public final B to(CharSequence to) throws XmppStringprepException {
return to(JidCreate.from(to));
}
/**
* Sets who the stanza is being sent "to". The XMPP protocol often makes the "to" attribute optional, so it does not
* always need to be set.
*
* @param to who the stanza is being sent to.
* @return a reference to this builder.
*/
public final B to(Jid to) {
this.to = to;
return getThis();
}
/**
* Sets who the the stanza is being sent "from".
*
* @param from who the stanza is being sent from.
* @return a reference to this builder.
* @throws XmppStringprepException if the provided character sequence is not a valid XMPP address.
* @see #from(Jid)
*/
public final B from(CharSequence from) throws XmppStringprepException {
return from(JidCreate.from(from));
}
/**
* Sets who the stanza is being sent "from". The XMPP protocol often makes the "from" attribute optional, so it does
* not always need to be set.
*
* @param from who the stanza is being sent from.
* @return a reference to this builder.
*/
public final B from(Jid from) {
this.from = from;
return getThis();
}
/**
* Sets the error for this stanza.
*
* @param stanzaError the error to associate with this stanza.
* @return a reference to this builder.
*/
public final B setError(StanzaError stanzaError) {
this.stanzaError = stanzaError;
return getThis();
}
/**
* Sets the xml:lang for this stanza.
*
* @param language the xml:lang of this stanza.
* @return a reference to this builder.
*/
public final B setLanguage(String language) {
this.language = language;
return getThis();
}
public final B addExtension(ExtensionElement extensionElement) {
QName key = extensionElement.getQName();
extensionElements.put(key, extensionElement);
return getThis();
}
public final B addExtensions(Collection<? extends ExtensionElement> extensionElements) {
for (ExtensionElement extensionElement : extensionElements) {
addExtension(extensionElement);
}
return getThis();
}
public final B overrideExtension(ExtensionElement extensionElement) {
QName key = extensionElement.getQName();
extensionElements.remove(key);
extensionElements.put(key, extensionElement);
return getThis();
}
public abstract B getThis();
@Override
public final String getStanzaId() {
return stanzaId;
}
@Override
public final Jid getTo() {
return to;
}
@Override
public final Jid getFrom() {
return from;
}
@Override
public final String getLanguage() {
return language;
}
@Override
public final StanzaError getError() {
return stanzaError;
}
@SuppressWarnings("unchecked")
@Override
public final <E extends ExtensionElement> E getExtension(QName qname) {
return (E) extensionElements.getFirst(qname);
}
@Override
public final List<ExtensionElement> getExtensions() {
return extensionElements.values();
}
@Override
public final List<ExtensionElement> getExtensions(QName qname) {
return extensionElements.getAll(qname);
}
@Override
public final <E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass) {
return XmppElementUtil.getElementsFrom(extensionElements, extensionElementClass);
}
public final boolean willBuildStanzaWithId() {
return stanzaIdSource != null || StringUtils.isNotEmpty(stanzaId);
}
public final void throwIfNoStanzaId() {
if (willBuildStanzaWithId()) {
return;
}
throw new IllegalArgumentException(
"The builder will not build a stanza with an ID set, although it is required");
}
protected abstract void addStanzaSpecificAttributes(ToStringUtil.Builder builder);
@Override
public final String toString() {
ToStringUtil.Builder builder = ToStringUtil.builderFor(getClass())
.addValue("id", stanzaId)
.addValue("from", from)
.addValue("to", to)
.addValue("language", language)
.addValue("error", stanzaError)
;
addStanzaSpecificAttributes(builder);
builder.add("Extension Elements", extensionElements.values(), e -> {
return e.getQName();
});
return builder.build();
}
public static MessageBuilder buildMessage() {
return buildMessage(null);
}
public static MessageBuilder buildMessage(String stanzaId) {
return new MessageBuilder(stanzaId);
}
public static MessageBuilder buildMessageFrom(Message message, String stanzaId) {
return new MessageBuilder(message, stanzaId);
}
public static MessageBuilder buildMessageFrom(Message message, StanzaIdSource stanzaIdSource) {
return new MessageBuilder(message, stanzaIdSource);
}
public static PresenceBuilder buildPresence() {
return buildPresence(null);
}
public static PresenceBuilder buildPresence(String stanzaId) {
return new PresenceBuilder(stanzaId);
}
public static PresenceBuilder buildPresenceFrom(Presence presence, String stanzaId) {
return new PresenceBuilder(presence, stanzaId);
}
public static PresenceBuilder buildPresenceFrom(Presence presence, StanzaIdSource stanzaIdSource) {
return new PresenceBuilder(presence, stanzaIdSource);
}
public static IqData buildIqData(String stanzaId) {
return new IqData(stanzaId);
}
public static <SB extends StanzaBuilder<?>> SB buildResponse(StanzaView request, Function<SB, String> builderFromStanzaId) {
SB responseBuilder = builderFromStanzaId.apply(request.getStanzaId());
responseBuilder.to(request.getFrom())
.from(request.getTo())
;
return responseBuilder;
}
}

View file

@ -106,7 +106,6 @@ public class StanzaError extends AbstractError implements ExtensionElement {
private final String conditionText; private final String conditionText;
private final String errorGenerator; private final String errorGenerator;
private final Type type; private final Type type;
private final Stanza stanza;
/** /**
* Creates a new error with the specified type, condition and message. * Creates a new error with the specified type, condition and message.
@ -120,13 +119,11 @@ public class StanzaError extends AbstractError implements ExtensionElement {
* @param errorGenerator TODO javadoc me please * @param errorGenerator TODO javadoc me please
* @param descriptiveTexts TODO javadoc me please * @param descriptiveTexts TODO javadoc me please
* @param extensions list of stanza extensions * @param extensions list of stanza extensions
* @param stanza the stanza carrying this XMPP error.
*/ */
public StanzaError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts, public StanzaError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
List<ExtensionElement> extensions, Stanza stanza) { List<ExtensionElement> extensions) {
super(descriptiveTexts, ERROR_CONDITION_AND_TEXT_NAMESPACE, extensions); super(descriptiveTexts, ERROR_CONDITION_AND_TEXT_NAMESPACE, extensions);
this.condition = Objects.requireNonNull(condition, "condition must not be null"); 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 // 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 // <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 // as conditionText, therefore reset it to null if it's the empty string
@ -184,16 +181,6 @@ public class StanzaError extends AbstractError implements ExtensionElement {
return conditionText; 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 @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("XMPPError: "); StringBuilder sb = new StringBuilder("XMPPError: ");
@ -271,7 +258,6 @@ public class StanzaError extends AbstractError implements ExtensionElement {
private String conditionText; private String conditionText;
private String errorGenerator; private String errorGenerator;
private Type type; private Type type;
private Stanza stanza;
private Builder() { private Builder() {
} }
@ -296,17 +282,11 @@ public class StanzaError extends AbstractError implements ExtensionElement {
return this; return this;
} }
public Builder setStanza(Stanza stanza) {
this.stanza = stanza;
return this;
}
public Builder copyFrom(StanzaError xmppError) { public Builder copyFrom(StanzaError xmppError) {
setCondition(xmppError.getCondition()); setCondition(xmppError.getCondition());
setType(xmppError.getType()); setType(xmppError.getType());
setConditionText(xmppError.getConditionText()); setConditionText(xmppError.getConditionText());
setErrorGenerator(xmppError.getErrorGenerator()); setErrorGenerator(xmppError.getErrorGenerator());
setStanza(xmppError.getStanza());
setDescriptiveTexts(xmppError.descriptiveTexts); setDescriptiveTexts(xmppError.descriptiveTexts);
setTextNamespace(xmppError.textNamespace); setTextNamespace(xmppError.textNamespace);
setExtensions(xmppError.extensions); setExtensions(xmppError.extensions);
@ -315,7 +295,7 @@ public class StanzaError extends AbstractError implements ExtensionElement {
public StanzaError build() { public StanzaError build() {
return new StanzaError(condition, conditionText, errorGenerator, type, descriptiveTexts, return new StanzaError(condition, conditionText, errorGenerator, type, descriptiveTexts,
extensions, stanza); extensions);
} }
@Override @Override

View file

@ -0,0 +1,53 @@
/**
*
* Copyright 2019 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 org.jivesoftware.smack.packet.id.StanzaIdSource;
public final class StanzaFactory {
private final StanzaIdSource stanzaIdSource;
StanzaIdSource getStanzaIdSource() {
return stanzaIdSource;
}
public StanzaFactory(StanzaIdSource stanzaIdSource) {
this.stanzaIdSource = stanzaIdSource;
}
public MessageBuilder buildMessageStanza() {
return new MessageBuilder(stanzaIdSource);
}
public MessageBuilder buildMessageStanzaFrom(Message message) {
return new MessageBuilder(message, stanzaIdSource);
}
public PresenceBuilder buildPresenceStanza() {
return new PresenceBuilder(stanzaIdSource);
}
public PresenceBuilder buildPresenceStanzaFrom(Presence presence) {
return new PresenceBuilder(presence, stanzaIdSource);
}
public IqData buildIqData() {
return new IqData(stanzaIdSource);
}
}

View file

@ -0,0 +1,102 @@
/**
*
* Copyright 2019 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.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.jid.Jid;
public interface StanzaView extends XmlLangElement {
/**
* Returns the unique ID of the stanza. The returned value could be <code>null</code>.
*
* @return the packet's unique ID or <code>null</code> if the id is not available.
*/
String getStanzaId();
/**
* Returns who the stanza is being sent "to", or <code>null</code> if
* the value is not set. The XMPP protocol often makes the "to"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the stanza is being sent to, or <code>null</code> if the
* value has not been set.
*/
Jid getTo();
/**
* Returns who the stanza is being sent "from" or <code>null</code> if
* the value is not set. The XMPP protocol often makes the "from"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the stanza is being sent from, or <code>null</code> if the
* value has not been set.
*/
Jid getFrom();
/**
* Returns the error associated with this packet, or <code>null</code> if there are
* no errors.
*
* @return the error sub-packet or <code>null</code> if there isn't an error.
*/
StanzaError getError();
<E extends ExtensionElement> E getExtension(QName qname);
default boolean hasExtension(QName qname) {
return getExtension(qname) != null;
}
/**
* Check if a extension element with the given namespace exists.
*
* @param namespace the namespace of the extension element to check for.
* @return true if a stanza extension exists, false otherwise.
*/
default boolean hasExtension(String namespace) {
for (ExtensionElement packetExtension : getExtensions()) {
if (packetExtension.getNamespace().equals(namespace)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
default <E extends ExtensionElement> E getExtension(Class<E> extensionElementClass) {
QName qname = XmppElementUtil.getQNameFor(extensionElementClass);
return (E) getExtension(qname);
}
/**
* Returns a list of all extension elements of this stanza.
*
* @return a list of all extension elements of this stanza.
*/
List<ExtensionElement> getExtensions();
List<ExtensionElement> getExtensions(QName qname);
<E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass);
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014-2018 Florian Schmaus * Copyright © 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -52,9 +52,15 @@ public class StartTls implements Nonza {
@Override @Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.rightAngleBracket();
xml.condEmptyElement(required, "required"); if (required) {
xml.closeElement(this); xml.rightAngleBracket();
xml.emptyElement("required");
xml.closeElement(this);
} else {
xml.closeEmptyElement();
}
return xml; return xml;
} }

View file

@ -18,7 +18,6 @@ package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public class XmlEnvironment { public class XmlEnvironment {

View file

@ -0,0 +1,28 @@
/**
*
* Copyright 2019 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;
public interface XmlLangElement {
/**
* Returns the xml:lang of this XML element, or null if one has not been set.
*
* @return the xml:lang of this XML element, or null.
*/
String getLanguage();
}

View file

@ -0,0 +1,54 @@
/**
*
* Copyright 2019 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.id;
import org.jivesoftware.smack.util.StringUtils;
public final class RandomStringStanzaIdSource {
public static class Factory implements StanzaIdSourceFactory {
private static final int REQUIRED_MIN_LENGTH = 10;
private final int length;
private final boolean verySecure;
public static final Factory VERY_SECURE = new Factory(10, true);
public static final Factory MEDIUM_SECURE = new Factory(10, false);
public Factory(int length, boolean verySecure) {
if (length < REQUIRED_MIN_LENGTH) {
throw new IllegalArgumentException(
"Insufficient length " + length + ", must be at least " + REQUIRED_MIN_LENGTH);
}
this.length = length;
this.verySecure = verySecure;
}
@Override
public StanzaIdSource constructStanzaIdSource() {
StanzaIdSource stanzaIdSource;
if (verySecure) {
stanzaIdSource = () -> StringUtils.randomString(length);
} else {
stanzaIdSource = () -> StringUtils.insecureRandomString(length);
}
return stanzaIdSource;
}
}
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2015 Florian Schmaus * Copyright 2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,20 +20,32 @@ import java.util.concurrent.atomic.AtomicLong;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
public class StanzaIdUtil { public class StandardStanzaIdSource implements StanzaIdSource {
public static final StandardStanzaIdSource DEFAULT = new StandardStanzaIdSource();
/** /**
* A prefix helps to make sure that ID's are unique across multiple instances. * A prefix helps to make sure that ID's are unique across multiple instances.
*/ */
private static final String PREFIX = StringUtils.randomString(5) + "-"; private final String prefix = StringUtils.randomString(5) + "-";
/** /**
* Keeps track of the current increment, which is appended to the prefix to * Keeps track of the current increment, which is appended to the prefix to
* forum a unique ID. * forum a unique ID.
*/ */
private static final AtomicLong ID = new AtomicLong(); private final AtomicLong id = new AtomicLong();
@Override
public String getNewStanzaId() {
return prefix + Long.toString(id.incrementAndGet());
}
public static class Factory implements StanzaIdSourceFactory {
@Override
public StandardStanzaIdSource constructStanzaIdSource() {
return new StandardStanzaIdSource();
}
public static String newStanzaId() {
return PREFIX + Long.toString(ID.incrementAndGet());
} }
} }

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 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.id;
public interface StanzaIdSource {
String getNewStanzaId();
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 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.id;
public interface StanzaIdSourceFactory {
StanzaIdSource constructStanzaIdSource();
}

View file

@ -0,0 +1,41 @@
/**
*
* Copyright 2019 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.id;
import java.util.UUID;
public final class UuidStanzaIdSource implements StanzaIdSource {
public static final UuidStanzaIdSource INSTANCE = new UuidStanzaIdSource();
private UuidStanzaIdSource() {
}
@Override
public String getNewStanzaId() {
return UUID.randomUUID().toString();
}
public static class Factory implements StanzaIdSourceFactory {
@Override
public UuidStanzaIdSource constructStanzaIdSource() {
return INSTANCE;
}
}
}

View file

@ -39,7 +39,7 @@ public interface ParsingExceptionCallback {
* Called when parsing a stanza caused an exception. * Called when parsing a stanza caused an exception.
* *
* @param stanzaData the raw stanza data that caused the exception * @param stanzaData the raw stanza data that caused the exception
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
void handleUnparsableStanza(UnparseableStanza stanzaData) throws IOException; void handleUnparsableStanza(UnparseableStanza stanzaData) throws IOException;

View file

@ -25,7 +25,6 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;

View file

@ -0,0 +1,45 @@
/**
*
* Copyright 2019 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.provider;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.jivesoftware.smack.packet.Element;
public class AbstractProvider<E extends Element> {
private final Class<E> elementClass;
@SuppressWarnings("unchecked")
protected AbstractProvider() {
Type currentType = getClass().getGenericSuperclass();
while (!(currentType instanceof ParameterizedType)) {
Class<?> currentClass = (Class<?>) currentType;
currentType = currentClass.getGenericSuperclass();
}
ParameterizedType parameterizedGenericSuperclass = (ParameterizedType) currentType;
Type[] actualTypeArguments = parameterizedGenericSuperclass.getActualTypeArguments();
Type elementType = actualTypeArguments[0];
elementClass = (Class<E>) elementType;
}
public final Class<E> getElementClass() {
return elementClass;
}
}

View file

@ -23,7 +23,6 @@ import java.io.IOException;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;

View file

@ -26,7 +26,6 @@ import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;

View file

@ -17,15 +17,53 @@
package org.jivesoftware.smack.provider; package org.jivesoftware.smack.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
/** /**
* <p>
* <b>Deprecation Notice:</b> This class is deprecated, use {@link IQProvider} instead.
* </p>
* An abstract class for parsing custom IQ packets. Each IQProvider must be registered with * An abstract class for parsing custom IQ packets. Each IQProvider must be registered with
* the ProviderManager class for it to be used. Every implementation of this * the ProviderManager class for it to be used. Every implementation of this
* abstract class <b>must</b> have a public, no-argument constructor. * abstract class <b>must</b> have a public, no-argument constructor.
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public abstract class IQProvider<I extends IQ> extends Provider<I> { public abstract class IQProvider<I extends IQ> extends IqProvider<I> {
public final I parse(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
return parse(parser, (XmlEnvironment) null);
}
public final I parse(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws IOException, XmlPullParserException, SmackParsingException {
// XPP3 calling convention assert: Parser should be at start tag
ParserUtils.assertAtStartTag(parser);
final int initialDepth = parser.getDepth();
final XmlEnvironment xmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
I e = parse(parser, initialDepth, xmlEnvironment);
// XPP3 calling convention assert: Parser should be at end tag of the consumed/parsed element
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
return e;
}
@Override
public final I parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
// Old-style IQ parsers do not need IqData.
return parse(parser, initialDepth, xmlEnvironment);
}
public abstract I parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException;
} }

View file

@ -34,7 +34,7 @@ public final class IQProviderInfo extends AbstractProviderInfo {
* @param namespace Namespace that provider parses. * @param namespace Namespace that provider parses.
* @param iqProvider The provider implementation. * @param iqProvider The provider implementation.
*/ */
public IQProviderInfo(String elementName, String namespace, IQProvider<IQ> iqProvider) { public IQProviderInfo(String elementName, String namespace, IqProvider<IQ> iqProvider) {
super(elementName, namespace, iqProvider); super(elementName, namespace, iqProvider);
} }
} }

View file

@ -23,7 +23,6 @@ import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;

View file

@ -0,0 +1,46 @@
/**
*
* Copyright 2019 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.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public abstract class IqProvider<I extends IQ> extends AbstractProvider<I> {
public final I parse(XmlPullParser parser, IqData iqCommon)
throws XmlPullParserException, IOException, SmackParsingException {
return parse(parser, iqCommon, null);
}
public final I parse(XmlPullParser parser, IqData iqData, XmlEnvironment outerXmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
final int initialDepth = parser.getDepth();
final XmlEnvironment xmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
return parse(parser, initialDepth, iqData, xmlEnvironment);
}
public abstract I parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException;
}

View file

@ -18,14 +18,11 @@
package org.jivesoftware.smack.provider; package org.jivesoftware.smack.provider;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
@ -40,27 +37,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
* @author Florian Schmaus * @author Florian Schmaus
* @param <E> the type of the resulting element. * @param <E> the type of the resulting element.
*/ */
public abstract class Provider<E extends Element> { public abstract class Provider<E extends Element> extends AbstractProvider<E> {
private final Class<E> elementClass;
@SuppressWarnings("unchecked")
protected Provider() {
Type currentType = getClass().getGenericSuperclass();
while (!(currentType instanceof ParameterizedType)) {
Class<?> currentClass = (Class<?>) currentType;
currentType = currentClass.getGenericSuperclass();
}
ParameterizedType parameterizedGenericSuperclass = (ParameterizedType) currentType;
Type[] actualTypeArguments = parameterizedGenericSuperclass.getActualTypeArguments();
Type elementType = actualTypeArguments[0];
elementClass = (Class<E>) elementType;
}
public final Class<E> getElementClass() {
return elementClass;
}
public final E parse(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException { public final E parse(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
return parse(parser, null); return parse(parser, null);

View file

@ -80,8 +80,8 @@ public class ProviderFileLoader implements ProviderLoader {
// an IQ class, add the class object itself, then we'll use // an IQ class, add the class object itself, then we'll use
// reflection later to create instances of the class. // reflection later to create instances of the class.
// Add the provider to the map. // Add the provider to the map.
if (IQProvider.class.isAssignableFrom(provider)) { if (IqProvider.class.isAssignableFrom(provider)) {
IQProvider<IQ> iqProvider = (IQProvider<IQ>) provider.getConstructor().newInstance(); IqProvider<IQ> iqProvider = (IqProvider<IQ>) provider.getConstructor().newInstance();
iqProviders.add(new IQProviderInfo(elementName, namespace, iqProvider)); iqProviders.add(new IQProviderInfo(elementName, namespace, iqProvider));
} }
else { else {

View file

@ -113,7 +113,7 @@ import org.jivesoftware.smack.util.XmppElementUtil;
public final class ProviderManager { public final class ProviderManager {
private static final Map<QName, ExtensionElementProvider<ExtensionElement>> extensionProviders = new ConcurrentHashMap<>(); private static final Map<QName, ExtensionElementProvider<ExtensionElement>> extensionProviders = new ConcurrentHashMap<>();
private static final Map<QName, IQProvider<IQ>> iqProviders = new ConcurrentHashMap<>(); private static final Map<QName, IqProvider<IQ>> iqProviders = new ConcurrentHashMap<>();
private static final Map<QName, ExtensionElementProvider<ExtensionElement>> streamFeatureProviders = new ConcurrentHashMap<>(); private static final Map<QName, ExtensionElementProvider<ExtensionElement>> streamFeatureProviders = new ConcurrentHashMap<>();
private static final Map<QName, NonzaProvider<? extends Nonza>> nonzaProviders = new ConcurrentHashMap<>(); private static final Map<QName, NonzaProvider<? extends Nonza>> nonzaProviders = new ConcurrentHashMap<>();
@ -167,7 +167,7 @@ public final class ProviderManager {
* @param namespace the XML namespace. * @param namespace the XML namespace.
* @return the IQ provider. * @return the IQ provider.
*/ */
public static IQProvider<IQ> getIQProvider(String elementName, String namespace) { public static IqProvider<IQ> getIQProvider(String elementName, String namespace) {
QName key = getQName(elementName, namespace); QName key = getQName(elementName, namespace);
return iqProviders.get(key); return iqProviders.get(key);
} }
@ -179,8 +179,8 @@ public final class ProviderManager {
* *
* @return all IQProvider instances. * @return all IQProvider instances.
*/ */
public static List<IQProvider<IQ>> getIQProviders() { public static List<IqProvider<IQ>> getIQProviders() {
List<IQProvider<IQ>> providers = new ArrayList<>(iqProviders.size()); List<IqProvider<IQ>> providers = new ArrayList<>(iqProviders.size());
providers.addAll(iqProviders.values()); providers.addAll(iqProviders.values());
return providers; return providers;
} }
@ -200,10 +200,10 @@ public final class ProviderManager {
validate(elementName, namespace); validate(elementName, namespace);
// First remove existing providers // First remove existing providers
QName key = removeIQProvider(elementName, namespace); QName key = removeIQProvider(elementName, namespace);
if (provider instanceof IQProvider) { if (provider instanceof IqProvider) {
iqProviders.put(key, (IQProvider<IQ>) provider); iqProviders.put(key, (IqProvider<IQ>) provider);
} else { } else {
throw new IllegalArgumentException("Provider must be an IQProvider"); throw new IllegalArgumentException("Provider must be an instance of IqProvider");
} }
} }

View file

@ -18,7 +18,6 @@ package org.jivesoftware.smack.provider;
import org.jivesoftware.smack.packet.TlsProceed; import org.jivesoftware.smack.packet.TlsProceed;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class TlsFailureProvider extends NonzaProvider<TlsProceed> { public final class TlsFailureProvider extends NonzaProvider<TlsProceed> {

View file

@ -18,7 +18,6 @@ package org.jivesoftware.smack.provider;
import org.jivesoftware.smack.packet.TlsFailure; import org.jivesoftware.smack.packet.TlsFailure;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class TlsProceedProvider extends NonzaProvider<TlsFailure> { public final class TlsProceedProvider extends NonzaProvider<TlsFailure> {

View file

@ -222,7 +222,7 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
* empty array here. * empty array here.
* *
* @return the initial response or null * @return the initial response or null
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
*/ */
protected abstract byte[] getAuthenticationText() throws SmackSaslException; protected abstract byte[] getAuthenticationText() throws SmackSaslException;

View file

@ -259,7 +259,7 @@ public abstract class ScramMechanism extends SASLMechanism {
/** /**
* *
* @return the Channel Binding data. * @return the Channel Binding data.
* @throws SmackSaslException if a SASL specific error occured. * @throws SmackSaslException if a SASL specific error occurred.
*/ */
protected byte[] getChannelBindingData() throws SmackSaslException { protected byte[] getChannelBindingData() throws SmackSaslException {
return null; return null;

View file

@ -110,7 +110,7 @@ public final class FileUtils {
* *
* @param file TODO javadoc me please * @param file TODO javadoc me please
* @return the content of file or null in case of an error * @return the content of file or null in case of an error
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
@SuppressWarnings("DefaultCharset") @SuppressWarnings("DefaultCharset")
public static String readFileOrThrow(File file) throws IOException { public static String readFileOrThrow(File file) throws IOException {

View file

@ -0,0 +1,23 @@
/**
*
* Copyright 2019 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.util;
public interface Function<R, T> {
R apply(T t);
}

View file

@ -34,7 +34,7 @@ import java.util.Set;
* @param <K> the type of the keys the map uses. * @param <K> the type of the keys the map uses.
* @param <V> the type of the values the map uses. * @param <V> the type of the values the map uses.
*/ */
public class MultiMap<K, V> { public class MultiMap<K, V> implements TypedCloneable<MultiMap<K, V>> {
/** /**
* The constant value {@value}. * The constant value {@value}.
@ -252,6 +252,19 @@ public class MultiMap<K, V> {
return new MultiMap<K, V>(Collections.unmodifiableMap(mapCopy)); return new MultiMap<K, V>(Collections.unmodifiableMap(mapCopy));
} }
@Override
public MultiMap<K, V> clone() {
Map<K, List<V>> clonedMap = new LinkedHashMap<>(map.size());
// TODO: Use Map.forEach() once Smack's minimum Android API is 24 or higher.
for (Entry<K, List<V>> entry : map.entrySet()) {
List<V> clonedList = CollectionUtil.newListWith(entry.getValue());
clonedMap.put(entry.getKey(), clonedList);
}
return new MultiMap<>(clonedMap);
}
private static final class SimpleMapEntry<K, V> implements Map.Entry<K, V> { private static final class SimpleMapEntry<K, V> implements Map.Entry<K, V> {
private final K key; private final K key;

View file

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.compress.packet.Compress; import org.jivesoftware.smack.compress.packet.Compress;
@ -35,10 +36,14 @@ import org.jivesoftware.smack.packet.EmptyResultIQ;
import org.jivesoftware.smack.packet.ErrorIQ; import org.jivesoftware.smack.packet.ErrorIQ;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.PresenceBuilder;
import org.jivesoftware.smack.packet.Session; import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StartTls; import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError; import org.jivesoftware.smack.packet.StreamError;
@ -47,7 +52,7 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IqProvider;
import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.xml.SmackXmlParser; import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -101,9 +106,9 @@ public class PacketParserUtils {
* @param parser TODO javadoc me please * @param parser TODO javadoc me please
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return a stanza which is either a Message, IQ or Presence. * @return a stanza which is either a Message, IQ or Presence.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public static Stanza parseStanza(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, SmackParsingException, IOException { public static Stanza parseStanza(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, SmackParsingException, IOException {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
@ -120,18 +125,25 @@ public class PacketParserUtils {
} }
} }
private static void parseCommonStanzaAttributes(Stanza stanza, XmlPullParser parser, XmlEnvironment xmlEnvironment) throws XmppStringprepException { private interface StanzaBuilderSupplier<SB extends StanzaBuilder<?>> {
SB get(String stanzaId);
}
private static <SB extends StanzaBuilder<?>> SB parseCommonStanzaAttributes(StanzaBuilderSupplier<SB> stanzaBuilderSupplier, XmlPullParser parser, XmlEnvironment xmlEnvironment) throws XmppStringprepException {
String id = parser.getAttributeValue("id"); String id = parser.getAttributeValue("id");
stanza.setStanzaId(id);
SB stanzaBuilder = stanzaBuilderSupplier.get(id);
Jid to = ParserUtils.getJidAttribute(parser, "to"); Jid to = ParserUtils.getJidAttribute(parser, "to");
stanza.setTo(to); stanzaBuilder.to(to);
Jid from = ParserUtils.getJidAttribute(parser, "from"); Jid from = ParserUtils.getJidAttribute(parser, "from");
stanza.setFrom(from); stanzaBuilder.from(from);
String language = ParserUtils.getXmlLang(parser, xmlEnvironment); String language = ParserUtils.getXmlLang(parser, xmlEnvironment);
stanza.setLanguage(language); stanzaBuilder.setLanguage(language);
return stanzaBuilder;
} }
public static Message parseMessage(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException { public static Message parseMessage(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
@ -144,8 +156,8 @@ public class PacketParserUtils {
* @param parser the XML parser, positioned at the start of a message packet. * @param parser the XML parser, positioned at the start of a message packet.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return a Message packet. * @return a Message packet.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static Message parseMessage(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public static Message parseMessage(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
@ -154,11 +166,14 @@ public class PacketParserUtils {
XmlEnvironment messageXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment messageXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
Message message = new Message();
parseCommonStanzaAttributes(message, parser, outerXmlEnvironment); MessageBuilder message = parseCommonStanzaAttributes(id -> {
return StanzaBuilder.buildMessage(id);
}, parser, outerXmlEnvironment);
String typeString = parser.getAttributeValue("", "type"); String typeString = parser.getAttributeValue("", "type");
if (typeString != null) { if (typeString != null) {
message.setType(Message.Type.fromString(typeString)); message.ofType(Message.Type.fromString(typeString));
} }
// Parse sub-elements. We include extra logic to make sure the values // Parse sub-elements. We include extra logic to make sure the values
@ -176,9 +191,8 @@ public class PacketParserUtils {
String xmlLangSubject = ParserUtils.getXmlLang(parser); String xmlLangSubject = ParserUtils.getXmlLang(parser);
String subject = parseElementText(parser); String subject = parseElementText(parser);
if (message.getSubject(xmlLangSubject) == null) { Message.Subject subjectExtensionElement = new Message.Subject(xmlLangSubject, subject);
message.addSubject(xmlLangSubject, subject); message.addExtension(subjectExtensionElement);
}
break; break;
case "thread": case "thread":
if (thread == null) { if (thread == null) {
@ -189,7 +203,8 @@ public class PacketParserUtils {
message.setError(parseError(parser, messageXmlEnvironment)); message.setError(parseError(parser, messageXmlEnvironment));
break; break;
default: default:
PacketParserUtils.addExtensionElement(message, parser, elementName, namespace, messageXmlEnvironment); ExtensionElement extensionElement = parseExtensionElement(elementName, namespace, parser, messageXmlEnvironment);
message.addExtension(extensionElement);
break; break;
} }
break; break;
@ -208,7 +223,7 @@ public class PacketParserUtils {
// situations where we have a body element with an explicit xml lang set and once where the value is inherited // situations where we have a body element with an explicit xml lang set and once where the value is inherited
// and both values are equal. // and both values are equal.
return message; return message.build();
} }
/** /**
@ -223,8 +238,8 @@ public class PacketParserUtils {
* *
* @param parser TODO javadoc me please * @param parser TODO javadoc me please
* @return the textual content of the element as String * @return the textual content of the element as String
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public static String parseElementText(XmlPullParser parser) throws XmlPullParserException, IOException { public static String parseElementText(XmlPullParser parser) throws XmlPullParserException, IOException {
assert parser.getEventType() == XmlPullParser.Event.START_ELEMENT; assert parser.getEventType() == XmlPullParser.Event.START_ELEMENT;
@ -262,8 +277,8 @@ public class PacketParserUtils {
* *
* @param parser the XML pull parser * @param parser the XML pull parser
* @return the element as string * @return the element as string
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public static CharSequence parseElement(XmlPullParser parser) throws XmlPullParserException, IOException { public static CharSequence parseElement(XmlPullParser parser) throws XmlPullParserException, IOException {
return parseElement(parser, false); return parseElement(parser, false);
@ -301,8 +316,8 @@ public class PacketParserUtils {
* @param depth TODO javadoc me please * @param depth TODO javadoc me please
* @param fullNamespaces TODO javadoc me please * @param fullNamespaces TODO javadoc me please
* @return the content of the current depth * @return the content of the current depth
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public static CharSequence parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException { public static CharSequence parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException {
if (parser.supportsRoundtrip()) { if (parser.supportsRoundtrip()) {
@ -427,8 +442,8 @@ public class PacketParserUtils {
* @param parser the XML parser, positioned at the start of a presence packet. * @param parser the XML parser, positioned at the start of a presence packet.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return a Presence packet. * @return a Presence packet.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static Presence parsePresence(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public static Presence parsePresence(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
@ -436,13 +451,16 @@ public class PacketParserUtils {
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
XmlEnvironment presenceXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment presenceXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
PresenceBuilder presence = parseCommonStanzaAttributes(
stanzaId -> StanzaBuilder.buildPresence(stanzaId), parser, outerXmlEnvironment);
Presence.Type type = Presence.Type.available; Presence.Type type = Presence.Type.available;
String typeString = parser.getAttributeValue("", "type"); String typeString = parser.getAttributeValue("", "type");
if (typeString != null && !typeString.equals("")) { if (typeString != null && !typeString.equals("")) {
type = Presence.Type.fromString(typeString); type = Presence.Type.fromString(typeString);
} }
Presence presence = new Presence(type);
parseCommonStanzaAttributes(presence, parser, outerXmlEnvironment); presence.ofType(type);
// Parse sub-elements // Parse sub-elements
outerloop: while (true) { outerloop: while (true) {
@ -468,9 +486,7 @@ public class PacketParserUtils {
// '<show />' element, which is a invalid XMPP presence // '<show />' element, which is a invalid XMPP presence
// stanza according to RFC 6121 4.7.2.1 // stanza according to RFC 6121 4.7.2.1
LOGGER.warning("Empty or null mode text in presence show element form " LOGGER.warning("Empty or null mode text in presence show element form "
+ presence.getFrom() + presence
+ " with id '"
+ presence.getStanzaId()
+ "' which is invalid according to RFC6121 4.7.2.1"); + "' which is invalid according to RFC6121 4.7.2.1");
} }
break; break;
@ -482,10 +498,10 @@ public class PacketParserUtils {
// Be extra robust: Skip PacketExtensions that cause Exceptions, instead of // Be extra robust: Skip PacketExtensions that cause Exceptions, instead of
// failing completely here. See SMACK-390 for more information. // failing completely here. See SMACK-390 for more information.
try { try {
PacketParserUtils.addExtensionElement(presence, parser, elementName, namespace, presenceXmlEnvironment); ExtensionElement extensionElement = parseExtensionElement(elementName, namespace, parser, presenceXmlEnvironment);
presence.addExtension(extensionElement);
} catch (Exception e) { } catch (Exception e) {
LOGGER.warning("Failed to parse extension element in Presence stanza: \"" + e + "\" from: '" LOGGER.log(Level.WARNING, "Failed to parse extension element in Presence stanza: " + presence, e);
+ presence.getFrom() + " id: '" + presence.getStanzaId() + "'");
} }
break; break;
} }
@ -500,7 +516,8 @@ public class PacketParserUtils {
break; break;
} }
} }
return presence;
return presence.build();
} }
public static IQ parseIQ(XmlPullParser parser) throws Exception { public static IQ parseIQ(XmlPullParser parser) throws Exception {
@ -513,9 +530,9 @@ public class PacketParserUtils {
* @param parser the XML parser, positioned at the start of an IQ packet. * @param parser the XML parser, positioned at the start of an IQ packet.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return an IQ object. * @return an IQ object.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws XmppStringprepException if the provided string is invalid. * @throws XmppStringprepException if the provided string is invalid.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static IQ parseIQ(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, XmppStringprepException, IOException, SmackParsingException { public static IQ parseIQ(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, XmppStringprepException, IOException, SmackParsingException {
@ -523,12 +540,19 @@ public class PacketParserUtils {
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
IQ iqPacket = null; IQ iqPacket = null;
StanzaError.Builder error = null; StanzaError error = null;
final String id = parser.getAttributeValue("", "id"); final String id = parser.getAttributeValue("", "id");
IqData iqData = StanzaBuilder.buildIqData(id);
final Jid to = ParserUtils.getJidAttribute(parser, "to"); final Jid to = ParserUtils.getJidAttribute(parser, "to");
iqData.to(to);
final Jid from = ParserUtils.getJidAttribute(parser, "from"); final Jid from = ParserUtils.getJidAttribute(parser, "from");
iqData.from(from);
final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type")); final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
iqData.ofType(type);
outerloop: while (true) { outerloop: while (true) {
XmlPullParser.Event eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
@ -544,9 +568,9 @@ public class PacketParserUtils {
// Otherwise, see if there is a registered provider for // Otherwise, see if there is a registered provider for
// this element name and namespace. // this element name and namespace.
default: default:
IQProvider<IQ> provider = ProviderManager.getIQProvider(elementName, namespace); IqProvider<IQ> provider = ProviderManager.getIQProvider(elementName, namespace);
if (provider != null) { if (provider != null) {
iqPacket = provider.parse(parser, outerXmlEnvironment); iqPacket = provider.parse(parser, iqData, outerXmlEnvironment);
} }
// Note that if we reach this code, it is guranteed that the result IQ contained a child element // Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first. // (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first.
@ -598,8 +622,8 @@ public class PacketParserUtils {
* *
* @param parser the XML parser, positioned at the start of the mechanisms stanza. * @param parser the XML parser, positioned at the start of the mechanisms stanza.
* @return a collection of Stings with the mechanisms included in the mechanisms stanza. * @return a collection of Stings with the mechanisms included in the mechanisms stanza.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
*/ */
public static Collection<String> parseMechanisms(XmlPullParser parser) public static Collection<String> parseMechanisms(XmlPullParser parser)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
@ -628,7 +652,7 @@ public class PacketParserUtils {
* *
* @param parser the XML parser, positioned at the start of the compression stanza. * @param parser the XML parser, positioned at the start of the compression stanza.
* @return The CompressionFeature stream element * @return The CompressionFeature stream element
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XmlPullParserException if an exception occurs while parsing the stanza. * @throws XmlPullParserException if an exception occurs while parsing the stanza.
*/ */
public static Compress.Feature parseCompressionFeature(XmlPullParser parser) public static Compress.Feature parseCompressionFeature(XmlPullParser parser)
@ -695,8 +719,8 @@ public class PacketParserUtils {
* @param parser the XML parser. * @param parser the XML parser.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return an stream error packet. * @return an stream error packet.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static StreamError parseStreamError(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public static StreamError parseStreamError(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
@ -747,7 +771,7 @@ public class PacketParserUtils {
return new StreamError(condition, conditionText, descriptiveTexts, extensions); return new StreamError(condition, conditionText, descriptiveTexts, extensions);
} }
public static StanzaError.Builder parseError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException { public static StanzaError parseError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
return parseError(parser, null); return parseError(parser, null);
} }
@ -757,11 +781,11 @@ public class PacketParserUtils {
* @param parser the XML parser. * @param parser the XML parser.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return an error sub-packet. * @return an error sub-packet.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static StanzaError.Builder parseError(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public static StanzaError parseError(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
Map<String, String> descriptiveTexts = null; Map<String, String> descriptiveTexts = null;
XmlEnvironment stanzaErrorXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment stanzaErrorXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
@ -808,7 +832,8 @@ public class PacketParserUtils {
} }
} }
builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts); builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts);
return builder;
return builder.build();
} }
/** /**
@ -820,8 +845,8 @@ public class PacketParserUtils {
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* *
* @return an extension element. * @return an extension element.
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
public static ExtensionElement parseExtensionElement(String elementName, String namespace, public static ExtensionElement parseExtensionElement(String elementName, String namespace,
@ -898,6 +923,18 @@ public class PacketParserUtils {
return new Session.Feature(optional); return new Session.Feature(optional);
} }
public static void addExtensionElement(StanzaBuilder<?> stanzaBuilder, XmlPullParser parser, XmlEnvironment outerXmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
ParserUtils.assertAtStartTag(parser);
addExtensionElement(stanzaBuilder, parser, parser.getName(), parser.getNamespace(), outerXmlEnvironment);
}
public static void addExtensionElement(StanzaBuilder<?> stanzaBuilder, XmlPullParser parser, String elementName,
String namespace, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
ExtensionElement extensionElement = parseExtensionElement(elementName, namespace, parser, outerXmlEnvironment);
stanzaBuilder.addExtension(extensionElement);
}
public static void addExtensionElement(Stanza packet, XmlPullParser parser, XmlEnvironment outerXmlEnvironment) public static void addExtensionElement(Stanza packet, XmlPullParser parser, XmlEnvironment outerXmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException { throws XmlPullParserException, IOException, SmackParsingException {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);

View file

@ -0,0 +1,24 @@
/**
*
* Copyright 2019 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.util;
// TODO: Replace with java.util.function.Predicate once Smack's minimum Android SDK level is 24 or higher.
public interface Predicate<T> {
boolean test(T t);
}

View file

@ -427,6 +427,13 @@ public class StringUtils {
return true; return true;
} }
public static boolean isNullOrNotEmpty(CharSequence cs) {
if (cs == null) {
return true;
}
return !cs.toString().isEmpty();
}
/** /**
* Returns true if the given CharSequence is empty. * Returns true if the given CharSequence is empty.
* *
@ -456,6 +463,11 @@ public class StringUtils {
*/ */
public static StringBuilder toStringBuilder(Collection<? extends Object> collection, String delimiter) { public static StringBuilder toStringBuilder(Collection<? extends Object> collection, String delimiter) {
StringBuilder sb = new StringBuilder(collection.size() * 20); StringBuilder sb = new StringBuilder(collection.size() * 20);
appendTo(collection, delimiter, sb);
return sb;
}
public static void appendTo(Collection<? extends Object> collection, String delimiter, StringBuilder sb) {
for (Iterator<? extends Object> it = collection.iterator(); it.hasNext();) { for (Iterator<? extends Object> it = collection.iterator(); it.hasNext();) {
Object cs = it.next(); Object cs = it.next();
sb.append(cs); sb.append(cs);
@ -463,7 +475,6 @@ public class StringUtils {
sb.append(delimiter); sb.append(delimiter);
} }
} }
return sb;
} }
public static String returnIfNotEmptyTrimmed(String string) { public static String returnIfNotEmptyTrimmed(String string) {
@ -524,7 +535,7 @@ public class StringUtils {
if (cs == null) { if (cs == null) {
return null; return null;
} }
if (cs.toString().isEmpty()) { if (isEmpty(cs)) {
throw new IllegalArgumentException(message); throw new IllegalArgumentException(message);
} }
return cs; return cs;

View file

@ -0,0 +1,75 @@
/**
*
* Copyright 2019 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.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ToStringUtil {
public static Builder builderFor(Class<?> clazz) {
StringBuilder sb = new StringBuilder();
sb.append(clazz.getSimpleName()).append('(');
return new Builder(sb);
}
public static final class Builder {
private final StringBuilder sb;
private Builder(StringBuilder sb) {
this.sb = sb;
}
public Builder addValue(String name, Object value) {
if (value == null) {
return this;
}
if (sb.charAt(sb.length() - 1) != '(') {
sb.append(' ');
}
sb.append(name).append("='").append(value).append('\'');
return this;
}
public <V> Builder add(String name, Collection<? extends V> values, Function<?, V> toStringFunction) {
if (values.isEmpty()) {
return this;
}
sb.append(' ').append(name).append('[');
List<String> stringValues = new ArrayList<>(values.size());
for (V value : values) {
String valueString = toStringFunction.apply(value).toString();
stringValues.add(valueString);
}
StringUtils.appendTo(stringValues, ", ", sb);
sb.append(']');
return this;
}
public String build() {
sb.append(')');
return sb.toString();
}
}
}

View file

@ -628,7 +628,7 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
* *
* @param writer TODO javadoc me please * @param writer TODO javadoc me please
* @param enclosingXmlEnvironment the enclosing XML environment. * @param enclosingXmlEnvironment the enclosing XML environment.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException { public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
try { try {

View file

@ -16,6 +16,9 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
@ -52,4 +55,21 @@ public class XmppElementUtil {
return new QName(namespace, element); return new QName(namespace, element);
} }
public static <E extends FullyQualifiedElement, R extends FullyQualifiedElement> List<R> getElementsFrom(
MultiMap<QName, E> elementMap, Class<R> extensionElementClass) {
QName qname = XmppElementUtil.getQNameFor(extensionElementClass);
List<E> extensionElements = elementMap.getAll(qname);
if (extensionElements.isEmpty()) {
return Collections.emptyList();
}
List<R> res = new ArrayList<>(extensionElements.size());
for (E extensionElement : extensionElements) {
R e = extensionElementClass.cast(extensionElement);
res.add(e);
}
return res;
}
} }

View file

@ -19,8 +19,8 @@ package org.jivesoftware.smack.filter;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.junit.Test; import org.junit.Test;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
@ -47,7 +47,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void autoCompareMatchingEntityFullJid() { public void autoCompareMatchingEntityFullJid() {
FromMatchesFilter filter = FromMatchesFilter.create(FULL_JID1_R1); FromMatchesFilter filter = FromMatchesFilter.create(FULL_JID1_R1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(FULL_JID1_R1); packet.setFrom(FULL_JID1_R1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -71,7 +71,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void autoCompareMatchingBaseJid() { public void autoCompareMatchingBaseJid() {
FromMatchesFilter filter = FromMatchesFilter.create(BASE_JID1); FromMatchesFilter filter = FromMatchesFilter.create(BASE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(BASE_JID1); packet.setFrom(BASE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -95,7 +95,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void autoCompareMatchingServiceJid() { public void autoCompareMatchingServiceJid() {
FromMatchesFilter filter = FromMatchesFilter.create(SERVICE_JID1); FromMatchesFilter filter = FromMatchesFilter.create(SERVICE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(SERVICE_JID1); packet.setFrom(SERVICE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -116,7 +116,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void bareCompareMatchingEntityFullJid() { public void bareCompareMatchingEntityFullJid() {
FromMatchesFilter filter = FromMatchesFilter.createBare(FULL_JID1_R1); FromMatchesFilter filter = FromMatchesFilter.createBare(FULL_JID1_R1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(BASE_JID1); packet.setFrom(BASE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -140,7 +140,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void bareCompareMatchingBaseJid() { public void bareCompareMatchingBaseJid() {
FromMatchesFilter filter = FromMatchesFilter.createBare(BASE_JID1); FromMatchesFilter filter = FromMatchesFilter.createBare(BASE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(BASE_JID1); packet.setFrom(BASE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -164,7 +164,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void bareCompareMatchingServiceJid() { public void bareCompareMatchingServiceJid() {
FromMatchesFilter filter = FromMatchesFilter.createBare(SERVICE_JID1); FromMatchesFilter filter = FromMatchesFilter.createBare(SERVICE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(SERVICE_JID1); packet.setFrom(SERVICE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -185,7 +185,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void fullCompareMatchingEntityFullJid() { public void fullCompareMatchingEntityFullJid() {
FromMatchesFilter filter = FromMatchesFilter.createFull(FULL_JID1_R1); FromMatchesFilter filter = FromMatchesFilter.createFull(FULL_JID1_R1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(FULL_JID1_R1); packet.setFrom(FULL_JID1_R1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -209,7 +209,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void fullCompareMatchingBaseJid() { public void fullCompareMatchingBaseJid() {
FromMatchesFilter filter = FromMatchesFilter.createFull(BASE_JID1); FromMatchesFilter filter = FromMatchesFilter.createFull(BASE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(BASE_JID1); packet.setFrom(BASE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));
@ -233,7 +233,7 @@ public class FromMatchesFilterTest {
@Test @Test
public void fullCompareMatchingServiceJid() { public void fullCompareMatchingServiceJid() {
FromMatchesFilter filter = FromMatchesFilter.createFull(SERVICE_JID1); FromMatchesFilter filter = FromMatchesFilter.createFull(SERVICE_JID1);
Stanza packet = new Message(); Stanza packet = StanzaBuilder.buildMessage().build();
packet.setFrom(SERVICE_JID1); packet.setFrom(SERVICE_JID1);
assertTrue(filter.accept(packet)); assertTrue(filter.accept(packet));

View file

@ -62,7 +62,7 @@ public class IQResponseTest {
*/ */
@Test @Test
public void testGeneratingValidErrorResponse() throws XmppStringprepException { public void testGeneratingValidErrorResponse() throws XmppStringprepException {
final StanzaError.Builder error = StanzaError.getBuilder(StanzaError.Condition.bad_request); final StanzaError error = StanzaError.getBuilder(StanzaError.Condition.bad_request).build();
final IQ request = new TestIQ(ELEMENT, NAMESPACE); final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.set); request.setType(IQ.Type.set);
@ -75,7 +75,7 @@ public class IQResponseTest {
assertNotNull(result.getStanzaId()); assertNotNull(result.getStanzaId());
assertEquals(request.getStanzaId(), result.getStanzaId()); assertEquals(request.getStanzaId(), result.getStanzaId());
assertEquals(request.getFrom(), result.getTo()); assertEquals(request.getFrom(), result.getTo());
assertEquals(error.build().toXML().toString(), result.getError().toXML().toString()); assertEquals(error.toXML().toString(), result.getError().toXML().toString());
// TODO this test was never valid // TODO this test was never valid
// assertEquals(CHILD_ELEMENT, result.getChildElementXML()); // assertEquals(CHILD_ELEMENT, result.getChildElementXML());
} }
@ -110,7 +110,7 @@ public class IQResponseTest {
*/ */
@Test @Test
public void testGeneratingErrorBasedOnError() throws XmppStringprepException { public void testGeneratingErrorBasedOnError() throws XmppStringprepException {
final StanzaError.Builder error = StanzaError.getBuilder(StanzaError.Condition.bad_request); final StanzaError error = StanzaError.getBuilder(StanzaError.Condition.bad_request).build();
final IQ request = new TestIQ(ELEMENT, NAMESPACE); final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.error); request.setType(IQ.Type.error);

View file

@ -18,7 +18,6 @@ package org.jivesoftware.smack.packet;
import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
@ -44,10 +43,12 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message messageTypeInConstructor = new Message(null, Message.Type.chat); Message messageBuildWithBuilder = StanzaBuilder.buildMessage()
messageTypeInConstructor.setStanzaId(null); .ofType(Message.Type.chat)
assertEquals(type, messageTypeInConstructor.getType()); .build();
assertXmlSimilar(control, messageTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
assertEquals(type, messageBuildWithBuilder.getType());
assertXmlSimilar(control, messageBuildWithBuilder.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
controlBuilder = new StringBuilder(); controlBuilder = new StringBuilder();
controlBuilder.append("<message") controlBuilder.append("<message")
@ -57,16 +58,18 @@ public class MessageTest {
.append("</message>"); .append("</message>");
control = controlBuilder.toString(); control = controlBuilder.toString();
Message messageTypeSet = getNewMessage(); Message messageTypeSet = StanzaBuilder.buildMessage()
messageTypeSet.setType(type2); .ofType(type2)
.build();
assertEquals(type2, messageTypeSet.getType()); assertEquals(type2, messageTypeSet.getType());
assertXmlSimilar(control, messageTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, messageTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void setNullMessageBodyTest() { public void setNullMessageBodyTest() {
Message message = getNewMessage(); StanzaBuilder.buildMessage()
message.addBody(null, null); .addBody(null, null)
.build();
} }
@Test @Test
@ -81,9 +84,9 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.setSubject(messageSubject); .setSubject(messageSubject)
.build();
assertEquals(messageSubject, message.getSubject()); assertEquals(messageSubject, message.getSubject());
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@ -100,9 +103,9 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.setBody(messageBody); .setBody(messageBody)
.build();
assertEquals(messageBody, message.getBody()); assertEquals(messageBody, message.getBody());
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@ -133,10 +136,11 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.addBody(null, messageBody1); .addBody(null, messageBody1)
message.addBody(lang2, messageBody2); .addBody(lang2, messageBody2)
message.addBody(lang3, messageBody3); .addBody(lang3, messageBody3)
.build();
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE)); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE));
Collection<String> languages = message.getBodyLanguages(); Collection<String> languages = message.getBodyLanguages();
@ -148,21 +152,20 @@ public class MessageTest {
} }
@Test @Test
public void removeMessageBodyTest() { public void simpleMessageBodyTest() {
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.setBody("test"); .setBody("test")
.build();
assertTrue(message.getBodies().size() == 1); assertTrue(message.getBodies().size() == 1);
message.setBody(null); message = StanzaBuilder.buildMessage().build();
assertTrue(message.getBodies().size() == 0); assertTrue(message.getBodies().size() == 0);
assertFalse(message.removeBody("sp"));
Message.Body body = message.addBody("es", "test"); message = StanzaBuilder.buildMessage()
.addBody("es", "test")
.build();
assertTrue(message.getBodies().size() == 1); assertTrue(message.getBodies().size() == 1);
message.removeBody(body);
assertTrue(message.getBodies().size() == 0);
} }
@Test @Test
@ -177,8 +180,9 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.setThread(messageThread); .setThread(messageThread)
.build();
assertEquals(messageThread, message.getThread()); assertEquals(messageThread, message.getThread());
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
@ -196,15 +200,10 @@ public class MessageTest {
.append("</message>"); .append("</message>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Message message = getNewMessage(); Message message = StanzaBuilder.buildMessage()
message.setLanguage(lang); .setLanguage(lang)
.build();
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
private static Message getNewMessage() {
Message message = new Message();
message.setStanzaId(null);
return message;
}
} }

View file

@ -41,8 +41,9 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Presence presenceTypeInConstructor = new Presence(type); Presence presenceTypeInConstructor = StanzaBuilder.buildPresence()
presenceTypeInConstructor.setStanzaId(null); .ofType(type)
.build();
assertEquals(type, presenceTypeInConstructor.getType()); assertEquals(type, presenceTypeInConstructor.getType());
assertXmlSimilar(control, presenceTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
@ -54,27 +55,27 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
control = controlBuilder.toString(); control = controlBuilder.toString();
Presence presenceTypeSet = getNewPresence(); PresenceBuilder presenceTypeSet = getNewPresence();
presenceTypeSet.setType(type2); presenceTypeSet.ofType(type2);
assertEquals(type2, presenceTypeSet.getType()); assertEquals(type2, presenceTypeSet.getType());
assertXmlSimilar(control, presenceTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceTypeSet.build().toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
public void setNullPresenceTypeTest() { public void setNullPresenceTypeTest() {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
getNewPresence().setType(null) getNewPresence().ofType(null)
); );
} }
@Test @Test
public void isPresenceAvailableTest() { public void isPresenceAvailableTest() {
Presence presence = getNewPresence(); PresenceBuilder presence = getNewPresence();
presence.setType(Presence.Type.available); presence.ofType(Presence.Type.available);
assertTrue(presence.isAvailable()); assertTrue(presence.build().isAvailable());
presence.setType(Presence.Type.unavailable); presence.ofType(Presence.Type.unavailable);
assertFalse(presence.isAvailable()); assertFalse(presence.build().isAvailable());
} }
@Test @Test
@ -89,11 +90,11 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Presence presence = getNewPresence(); PresenceBuilder presence = getNewPresence();
presence.setStatus(status); presence.setStatus(status);
assertEquals(status, presence.getStatus()); assertEquals(status, presence.getStatus());
assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.build().toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -108,11 +109,11 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Presence presence = getNewPresence(); PresenceBuilder presence = getNewPresence();
presence.setPriority(priority); presence.setPriority(priority);
assertEquals(priority, presence.getPriority()); assertEquals(priority, presence.getPriority());
assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.build().toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -143,11 +144,14 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Presence presenceModeInConstructor = new Presence(Presence.Type.available, status, priority, Presence presenceBuildWithBuilder = StanzaBuilder.buildPresence()
mode1); .ofType(Presence.Type.available)
presenceModeInConstructor.setStanzaId(null); .setStatus(status)
assertEquals(mode1, presenceModeInConstructor.getMode()); .setPriority(priority)
assertXmlSimilar(control, presenceModeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); .setMode(mode1)
.build();
assertEquals(mode1, presenceBuildWithBuilder.getMode());
assertXmlSimilar(control, presenceBuildWithBuilder.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
controlBuilder = new StringBuilder(); controlBuilder = new StringBuilder();
controlBuilder.append("<presence>") controlBuilder.append("<presence>")
@ -157,20 +161,20 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
control = controlBuilder.toString(); control = controlBuilder.toString();
Presence presenceModeSet = getNewPresence(); PresenceBuilder presenceModeSet = getNewPresence();
presenceModeSet.setMode(mode2); presenceModeSet.setMode(mode2);
assertEquals(mode2, presenceModeSet.getMode()); assertEquals(mode2, presenceModeSet.getMode());
assertXmlSimilar(control, presenceModeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceModeSet.build().toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
public void isModeAwayTest() { public void isModeAwayTest() {
Presence presence = getNewPresence(); PresenceBuilder presence = getNewPresence();
presence.setMode(Presence.Mode.away); presence.setMode(Presence.Mode.away);
assertTrue(presence.isAway()); assertTrue(presence.build().isAway());
presence.setMode(Presence.Mode.chat); presence.setMode(Presence.Mode.chat);
assertFalse(presence.isAway()); assertFalse(presence.build().isAway());
} }
@Test @Test
@ -185,15 +189,14 @@ public class PresenceTest {
.append("</presence>"); .append("</presence>");
String control = controlBuilder.toString(); String control = controlBuilder.toString();
Presence presence = getNewPresence(); PresenceBuilder presence = getNewPresence();
presence.setLanguage(lang); presence.setLanguage(lang);
assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.build().toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
private static Presence getNewPresence() { private static PresenceBuilder getNewPresence() {
Presence presence = new Presence(Presence.Type.available); PresenceBuilder presence = StanzaBuilder.buildPresence().ofType(Presence.Type.available);
presence.setStanzaId(null);
return presence; return presence;
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2016-2017 Florian Schmaus * Copyright © 2016-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,8 +18,6 @@ package org.jivesoftware.smack.packet;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import org.jivesoftware.smack.packet.Presence.Mode;
import org.junit.Test; import org.junit.Test;
import org.jxmpp.jid.JidTestUtil; import org.jxmpp.jid.JidTestUtil;
@ -27,15 +25,21 @@ public class ToStringTest {
@Test @Test
public void messageTest() { public void messageTest() {
Message message = new Message(JidTestUtil.BARE_JID_1, Message.Type.headline); Message message = StanzaBuilder.buildMessage("message-id")
message.setStanzaId("message-id"); .ofType(Message.Type.headline)
.to(JidTestUtil.BARE_JID_1)
.build();
String string = message.toString(); String string = message.toString();
assertEquals("Message Stanza [to=one@exampleone.org,id=message-id,type=headline,]", string); assertEquals("Message Stanza [to=one@exampleone.org,id=message-id,type=headline,]", string);
} }
@Test @Test
public void presenceTest() { public void presenceTest() {
Presence presence = new Presence(Presence.Type.subscribe, null, 0, Mode.away); Presence presence = StanzaBuilder.buildPresence()
.ofType(Presence.Type.subscribe)
.setPriority(0)
.setMode(Presence.Mode.away)
.build();
presence.setStanzaId("presence-id"); presence.setStanzaId("presence-id");
String string = presence.toString(); String string = presence.toString();
assertEquals("Presence Stanza [id=presence-id,type=subscribe,mode=away,prio=0,]", string); assertEquals("Presence Stanza [id=presence-id,type=subscribe,mode=away,prio=0,]", string);

View file

@ -16,12 +16,13 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import static org.jivesoftware.smack.packet.StanzaError.Condition;
import static org.jivesoftware.smack.packet.StanzaError.Type;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.Map; import java.util.Map;
import org.jivesoftware.smack.packet.StanzaError.Condition;
import org.jivesoftware.smack.packet.StanzaError.Type;
import org.junit.Test; import org.junit.Test;
public class XMPPErrorTest { public class XMPPErrorTest {

View file

@ -45,7 +45,6 @@ import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import com.jamesmurty.utils.XMLBuilder; import com.jamesmurty.utils.XMLBuilder;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -547,30 +546,6 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().contains(otherLanguage)); assertTrue(message.getSubjectLanguages().contains(otherLanguage));
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has default language, first subject no language, second subject default language
control = XMLBuilder.create("message")
.a("from", "romeo@montague.lit/orchard")
.a("to", "juliet@capulet.lit/balcony")
.a("id", "zid615d9")
.a("type", "chat")
.a("xml:lang", defaultLanguage)
.e("subject")
.t(defaultLanguage)
.up()
.e("subject")
.a("xml:lang", defaultLanguage)
.t(defaultLanguage + "2")
.asString(outputProperties);
message = PacketParserUtils
.parseMessage(PacketParserUtils.getParserFor(control));
assertEquals(defaultLanguage, message.getSubject());
assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
assertEquals(1, message.getSubjects().size());
assertEquals(0, message.getSubjectLanguages().size());
assertXmlNotSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has non-default language, first subject no language, second subject default language // message has non-default language, first subject no language, second subject default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
.a("from", "romeo@montague.lit/orchard") .a("from", "romeo@montague.lit/orchard")
@ -867,7 +842,7 @@ public class PacketParserUtilsTest {
.element("text", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).t(text).up() .element("text", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).t(text).up()
.asString(); .asString();
XmlPullParser parser = TestUtils.getParser(errorXml); XmlPullParser parser = TestUtils.getParser(errorXml);
StanzaError error = PacketParserUtils.parseError(parser).build(); StanzaError error = PacketParserUtils.parseError(parser);
assertEquals(text, error.getDescriptiveText()); assertEquals(text, error.getDescriptiveText());
} }
} }

View file

@ -120,18 +120,10 @@ public final class ChatMarkersManager extends Manager {
chatManager = ChatManager.getInstanceFor(connection); chatManager = ChatManager.getInstanceFor(connection);
connection.addStanzaInterceptor(new StanzaListener() { connection.addMessageInterceptor(mb -> mb.addExtension(ChatMarkersElements.MarkableExtension.INSTANCE),
@Override m -> {
public void processStanza(Stanza packet) return OUTGOING_MESSAGE_FILTER.accept(m);
throws });
NotConnectedException,
InterruptedException,
SmackException.NotLoggedInException {
Message message = (Message) packet;
// add a markable extension
message.addExtension(ChatMarkersElements.MarkableExtension.INSTANCE);
}
}, OUTGOING_MESSAGE_FILTER);
connection.addSyncStanzaListener(new StanzaListener() { connection.addSyncStanzaListener(new StanzaListener() {
@Override @Override

View file

@ -88,10 +88,11 @@ public final class DnsOverXmppManager extends Manager {
try { try {
response = resolver.resolve(query); response = resolver.resolve(query);
} catch (IOException exception) { } catch (IOException exception) {
StanzaError.Builder errorBuilder = StanzaError.getBuilder() StanzaError errorBuilder = StanzaError.getBuilder()
.setType(Type.CANCEL) .setType(Type.CANCEL)
.setCondition(Condition.internal_server_error) .setCondition(Condition.internal_server_error)
.setDescriptiveEnText("Exception while resolving your DNS query", exception) .setDescriptiveEnText("Exception while resolving your DNS query", exception)
.build()
; ;
IQ errorResponse = IQ.createErrorResponse(iqRequest, errorBuilder); IQ errorResponse = IQ.createErrorResponse(iqRequest, errorBuilder);

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2017 Florian Schmaus * Copyright 2017-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,8 +20,12 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.MessageView;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -33,6 +37,8 @@ public class ExplicitMessageEncryptionElement implements ExtensionElement {
public static final String NAMESPACE = "urn:xmpp:eme:0"; public static final String NAMESPACE = "urn:xmpp:eme:0";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public enum ExplicitMessageEncryptionProtocol { public enum ExplicitMessageEncryptionProtocol {
/** /**
@ -154,14 +160,12 @@ public class ExplicitMessageEncryptionElement implements ExtensionElement {
* @param protocolNamespace namespace * @param protocolNamespace namespace
* @return true if message has EME element for that namespace, otherwise false * @return true if message has EME element for that namespace, otherwise false
*/ */
public static boolean hasProtocol(Message message, String protocolNamespace) { public static boolean hasProtocol(MessageView message, String protocolNamespace) {
List<ExtensionElement> extensionElements = message.getExtensions( List<ExplicitMessageEncryptionElement> emeElements = message
ExplicitMessageEncryptionElement.ELEMENT, .getExtensions(ExplicitMessageEncryptionElement.class);
ExplicitMessageEncryptionElement.NAMESPACE);
for (ExtensionElement extensionElement : extensionElements) { for (ExplicitMessageEncryptionElement emeElement : emeElements) {
ExplicitMessageEncryptionElement e = (ExplicitMessageEncryptionElement) extensionElement; if (emeElement.getEncryptionNamespace().equals(protocolNamespace)) {
if (e.getEncryptionNamespace().equals(protocolNamespace)) {
return true; return true;
} }
} }
@ -176,7 +180,7 @@ public class ExplicitMessageEncryptionElement implements ExtensionElement {
* @param protocol protocol * @param protocol protocol
* @return true if message has EME element for that namespace, otherwise false * @return true if message has EME element for that namespace, otherwise false
*/ */
public static boolean hasProtocol(Message message, ExplicitMessageEncryptionProtocol protocol) { public static boolean hasProtocol(MessageView message, ExplicitMessageEncryptionProtocol protocol) {
return hasProtocol(message, protocol.namespace); return hasProtocol(message, protocol.namespace);
} }
@ -184,10 +188,10 @@ public class ExplicitMessageEncryptionElement implements ExtensionElement {
* Add an EME element containing the specified {@code protocol} namespace to the message. * Add an EME element containing the specified {@code protocol} namespace to the message.
* In case there is already an element with that protocol, we do nothing. * In case there is already an element with that protocol, we do nothing.
* *
* @param message message * @param message a message builder.
* @param protocol encryption protocol * @param protocol encryption protocol
*/ */
public static void set(Message message, ExplicitMessageEncryptionProtocol protocol) { public static void set(MessageBuilder message, ExplicitMessageEncryptionProtocol protocol) {
if (!hasProtocol(message, protocol.namespace)) { if (!hasProtocol(message, protocol.namespace)) {
message.addExtension(new ExplicitMessageEncryptionElement(protocol)); message.addExtension(new ExplicitMessageEncryptionElement(protocol));
} }

View file

@ -43,6 +43,7 @@ import java.util.WeakHashMap;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.util.SecurityUtil; import org.jivesoftware.smack.util.SecurityUtil;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.hashes.element.HashElement; import org.jivesoftware.smackx.hashes.element.HashElement;

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2017 Florian Schmaus * Copyright 2017-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,10 @@
*/ */
package org.jivesoftware.smackx.hints.element; package org.jivesoftware.smackx.hints.element;
import org.jivesoftware.smack.packet.Message; import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.MessageView;
/** /**
* A "store" hint. Messages with this hint should be stored in permanent stores or archives. * A "store" hint. Messages with this hint should be stored in permanent stores or archives.
@ -29,6 +32,8 @@ public final class StoreHint extends MessageProcessingHint {
public static final String ELEMENT = "store"; public static final String ELEMENT = "store";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private StoreHint() { private StoreHint() {
} }
@ -47,15 +52,15 @@ public final class StoreHint extends MessageProcessingHint {
return MessageProcessingHintType.store; return MessageProcessingHintType.store;
} }
public static StoreHint from(Message message) { public static StoreHint from(MessageView message) {
return message.getExtension(ELEMENT, NAMESPACE); return message.getExtension(QNAME);
} }
public static boolean hasHint(Message message) { public static boolean hasHint(MessageView message) {
return from(message) != null; return from(message) != null;
} }
public static void set(Message message) { public static void set(MessageBuilder message) {
message.overrideExtension(INSTANCE); message.overrideExtension(INSTANCE);
} }
} }

View file

@ -55,8 +55,8 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
* *
* @param parser parser * @param parser parser
* @return HeadersExtension or null if no headers * @return HeadersExtension or null if no headers
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input. * @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
*/ */
protected HeadersExtension parseHeaders(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException { protected HeadersExtension parseHeaders(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
@ -76,8 +76,8 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
* @param parser parser * @param parser parser
* @return Data or null if no data * @return Data or null if no data
* *
* @throws XmlPullParserException if an error in the XML parser occured. * @throws XmlPullParserException if an error in the XML parser occurred.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
protected AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws XmlPullParserException, IOException { protected AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws XmlPullParserException, IOException {
NamedElement child = null; NamedElement child = null;

View file

@ -251,7 +251,7 @@ public final class HttpFileUploadManager extends Manager {
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @throws XMPPException.XMPPErrorException if there was an XMPP error returned. * @throws XMPPException.XMPPErrorException if there was an XMPP error returned.
* @throws SmackException if Smack detected an exceptional situation. * @throws SmackException if Smack detected an exceptional situation.
* @throws IOException if an I/O error occured. * @throws IOException if an I/O error occurred.
*/ */
public URL uploadFile(File file, UploadProgressListener listener) throws InterruptedException, public URL uploadFile(File file, UploadProgressListener listener) throws InterruptedException,
XMPPException.XMPPErrorException, SmackException, IOException { XMPPException.XMPPErrorException, SmackException, IOException {

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2016 Florian Schmaus * Copyright © 2016-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,20 +16,26 @@
*/ */
package org.jivesoftware.smackx.iot.control.element; package org.jivesoftware.smackx.iot.control.element;
import org.jivesoftware.smack.packet.AbstractIqBuilder;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IqData;
public class IoTSetResponse extends IQ { public class IoTSetResponse extends IQ {
public static final String ELEMENT = "setResponse"; public static final String ELEMENT = "setResponse";
public static final String NAMESPACE = Constants.IOT_CONTROL_NAMESPACE; public static final String NAMESPACE = Constants.IOT_CONTROL_NAMESPACE;
public IoTSetResponse(IqData iqBuilder) {
super(iqBuilder, ELEMENT, NAMESPACE);
}
// TODO: Deprecate when stanza build is ready.
public IoTSetResponse() { public IoTSetResponse() {
super(ELEMENT, NAMESPACE); super(ELEMENT, NAMESPACE);
} }
public IoTSetResponse(IoTSetRequest iotSetRequest) { public IoTSetResponse(IoTSetRequest iotSetRequest) {
this(); this(AbstractIqBuilder.createResponse(iotSetRequest));
initializeAsResultFor(iotSetRequest);
} }
@Override @Override

View file

@ -125,10 +125,14 @@ public final class IoTDataManager extends IoTManager {
@Override @Override
public void momentaryReadOut(List<? extends IoTDataField> results) { public void momentaryReadOut(List<? extends IoTDataField> results) {
IoTFieldsExtension iotFieldsExtension = IoTFieldsExtension.buildFor(dataRequest.getSequenceNr(), true, thing.getNodeInfo(), results); IoTFieldsExtension iotFieldsExtension = IoTFieldsExtension.buildFor(dataRequest.getSequenceNr(), true, thing.getNodeInfo(), results);
Message message = new Message(dataRequest.getFrom());
message.addExtension(iotFieldsExtension); XMPPConnection connection = connection();
Message message = connection.getStanzaFactory().buildMessageStanza()
.to(dataRequest.getFrom())
.addExtension(iotFieldsExtension)
.build();
try { try {
connection().sendStanza(message); connection.sendStanza(message);
} }
catch (NotConnectedException | InterruptedException e) { catch (NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send read-out response " + message, e); LOGGER.log(Level.SEVERE, "Could not send read-out response " + message, e);

View file

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.iot.element.NodeInfo; import org.jivesoftware.smackx.iot.element.NodeInfo;
public class NodeElement extends IoTDataExtensionElement { public class NodeElement extends IoTDataExtensionElement {

View file

@ -140,8 +140,10 @@ public final class IoTProvisioningManager extends Manager {
+ " is already not subscribed to our presence."); + " is already not subscribed to our presence.");
return; return;
} }
Presence unsubscribed = new Presence(Presence.Type.unsubscribed); Presence unsubscribed = connection.getStanzaFactory().buildPresenceStanza()
unsubscribed.setTo(unfriendJid); .ofType(Presence.Type.unsubscribed)
.to(unfriendJid)
.build();
connection.sendStanza(unsubscribed); connection.sendStanza(unsubscribed);
} }
}, UNFRIEND_MESSAGE); }, UNFRIEND_MESSAGE);
@ -162,7 +164,10 @@ public final class IoTProvisioningManager extends Manager {
// friendship requests. // friendship requests.
final XMPPConnection connection = connection(); final XMPPConnection connection = connection();
Friend friendNotification = new Friend(connection.getUser().asBareJid()); Friend friendNotification = new Friend(connection.getUser().asBareJid());
Message notificationMessage = new Message(friendJid, friendNotification); Message notificationMessage = connection.getStanzaFactory().buildMessageStanza()
.to(friendJid)
.addExtension(friendNotification)
.build();
connection.sendStanza(notificationMessage); connection.sendStanza(notificationMessage);
} else { } else {
// Check is the message was send from a thing we previously // Check is the message was send from a thing we previously
@ -359,8 +364,11 @@ public final class IoTProvisioningManager extends Manager {
} }
public void sendFriendshipRequest(BareJid bareJid) throws NotConnectedException, InterruptedException { public void sendFriendshipRequest(BareJid bareJid) throws NotConnectedException, InterruptedException {
Presence presence = new Presence(Presence.Type.subscribe); XMPPConnection connection = connection();
presence.setTo(bareJid); Presence presence = connection.getStanzaFactory().buildPresenceStanza()
.ofType(Presence.Type.subscribe)
.to(bareJid)
.build();
friendshipRequestedCache.put(bareJid, null); friendshipRequestedCache.put(bareJid, null);
@ -379,9 +387,12 @@ public final class IoTProvisioningManager extends Manager {
public void unfriend(Jid friend) throws NotConnectedException, InterruptedException { public void unfriend(Jid friend) throws NotConnectedException, InterruptedException {
if (isMyFriend(friend)) { if (isMyFriend(friend)) {
Presence presence = new Presence(Presence.Type.unsubscribed); XMPPConnection connection = connection();
presence.setTo(friend); Presence presence = connection.getStanzaFactory().buildPresenceStanza()
connection().sendStanza(presence); .ofType(Presence.Type.unsubscribed)
.to(friend)
.build();
connection.sendStanza(presence);
} }
} }

View file

@ -23,6 +23,7 @@ import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.jid_prep.element.JidPrepIq; import org.jivesoftware.smackx.jid_prep.element.JidPrepIq;
public class JidPrepIqProvider extends IQProvider<JidPrepIq> { public class JidPrepIqProvider extends IQProvider<JidPrepIq> {

View file

@ -35,6 +35,7 @@ import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.muclight.element.MUCLightAffiliationsIQ; import org.jivesoftware.smackx.muclight.element.MUCLightAffiliationsIQ;
@ -126,9 +127,9 @@ public class MultiUserChatLight {
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
*/ */
public void sendMessage(String text) throws NotConnectedException, InterruptedException { public void sendMessage(String text) throws NotConnectedException, InterruptedException {
Message message = createMessage(); MessageBuilder message = buildMessage();
message.setBody(text); message.setBody(text);
connection.sendStanza(message); connection.sendStanza(message.build());
} }
/** /**
@ -157,9 +158,28 @@ public class MultiUserChatLight {
* Creates a new Message to send to the chat room. * Creates a new Message to send to the chat room.
* *
* @return a new Message addressed to the chat room. * @return a new Message addressed to the chat room.
* @deprecated use {@link #buildMessage()} instead.
*/ */
@Deprecated
// TODO: Remove when stanza builder is ready.
public Message createMessage() { public Message createMessage() {
return new Message(room, Message.Type.groupchat); return connection.getStanzaFactory().buildMessageStanza()
.ofType(Message.Type.groupchat)
.to(room)
.build();
}
/**
* Constructs a new message builder for messages send to this MUC room.
*
* @return a new message builder.
*/
public MessageBuilder buildMessage() {
return connection.getStanzaFactory()
.buildMessageStanza()
.ofType(Message.Type.groupchat)
.to(room)
;
} }
/** /**
@ -169,10 +189,23 @@ public class MultiUserChatLight {
* the message. * the message.
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
* @deprecated use {@link #sendMessage(MessageBuilder)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public void sendMessage(Message message) throws NotConnectedException, InterruptedException { public void sendMessage(Message message) throws NotConnectedException, InterruptedException {
message.setTo(room); sendMessage(message.asBuilder());
message.setType(Message.Type.groupchat); }
/**
* Sends a Message to the chat room.
*
* @param messageBuilder the message.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
public void sendMessage(MessageBuilder messageBuilder) throws NotConnectedException, InterruptedException {
Message message = messageBuilder.to(room).ofType(Message.Type.groupchat).build();
connection.sendStanza(message); connection.sendStanza(message);
} }

View file

@ -50,7 +50,9 @@ public class RemoteDisablingProvider extends ExtensionElementProvider<RemoteDisa
String affiliation = parser.getAttributeValue("", "affiliation"); String affiliation = parser.getAttributeValue("", "affiliation");
if (affiliation == null || !affiliation.equals("none")) { if (affiliation == null || !affiliation.equals("none")) {
return null; // TODO: Is this correct? We previously returned null here, but was certainly wrong, as
// providers should always return an element or throw.
throw new IOException("Invalid affiliation: " + affiliation);
} }
} }
} else if (eventType == XmlPullParser.Event.END_ELEMENT) { } else if (eventType == XmlPullParser.Event.END_ELEMENT) {

View file

@ -21,7 +21,6 @@ import java.util.WeakHashMap;
import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
@ -31,7 +30,9 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter; import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.Predicate;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.sid.element.OriginIdElement; import org.jivesoftware.smackx.sid.element.OriginIdElement;
@ -62,12 +63,12 @@ public final class StableUniqueStanzaIdManager extends Manager {
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE); private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas. // Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
private static final StanzaListener ADD_ORIGIN_ID_INTERCEPTOR = new StanzaListener() { private static final Consumer<MessageBuilder> ADD_ORIGIN_ID_INTERCEPTOR = mb -> OriginIdElement.addOriginId(mb);
@Override
public void processStanza(Stanza stanza) { // We need a filter for outgoing messages that do not carry an origin-id already.
Message message = (Message) stanza; private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER));
OriginIdElement.addOriginId(message); private static final Predicate<Message> ADD_ORIGIN_ID_PREDICATE = m -> {
} return ADD_ORIGIN_ID_FILTER.accept(m);
}; };
static { static {
@ -112,10 +113,8 @@ public final class StableUniqueStanzaIdManager extends Manager {
* Start appending origin-id elements to outgoing stanzas and add the feature to disco. * Start appending origin-id elements to outgoing stanzas and add the feature to disco.
*/ */
public synchronized void enable() { public synchronized void enable() {
connection().addMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, ADD_ORIGIN_ID_PREDICATE);
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
// We need a filter for outgoing messages that do not carry an origin-id already
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER));
connection().addStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, filter);
} }
/** /**
@ -123,7 +122,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
*/ */
public synchronized void disable() { public synchronized void disable() {
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
connection().removeStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR); connection().removeMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR);
} }
/** /**

View file

@ -17,6 +17,7 @@
package org.jivesoftware.smackx.sid.element; package org.jivesoftware.smackx.sid.element;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager; import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
@ -38,7 +39,10 @@ public class OriginIdElement extends StableAndUniqueIdElement {
* *
* @param message message. * @param message message.
* @return the added origin-id element. * @return the added origin-id element.
* @deprecated use {@link #addOriginId(MessageBuilder)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public static OriginIdElement addOriginId(Message message) { public static OriginIdElement addOriginId(Message message) {
OriginIdElement originId = new OriginIdElement(); OriginIdElement originId = new OriginIdElement();
message.addExtension(originId); message.addExtension(originId);
@ -47,6 +51,20 @@ public class OriginIdElement extends StableAndUniqueIdElement {
return originId; return originId;
} }
/**
* Add an origin-id element to a message and set the stanzas id to the same id as in the origin-id element.
*
* @param messageBuilder the message builder to add an origin ID to.
* @return the added origin-id element.
*/
public static OriginIdElement addOriginId(MessageBuilder messageBuilder) {
OriginIdElement originId = new OriginIdElement();
messageBuilder.addExtension(originId);
// TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID.
// message.setStanzaId(originId.getId());
return originId;
}
/** /**
* Return true, if the message contains a origin-id element. * Return true, if the message contains a origin-id element.
* *

View file

@ -21,6 +21,7 @@ import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.FormFieldChildElement; import org.jivesoftware.smackx.xdata.FormFieldChildElement;

View file

@ -26,6 +26,7 @@ import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProvider; import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProvider;
import org.jivesoftware.smackx.xmlelement.element.DataFormsXmlElement; import org.jivesoftware.smackx.xmlelement.element.DataFormsXmlElement;

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -28,7 +29,6 @@ import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements.Acknowle
import org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider; import org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.jxmpp.jid.impl.JidCreate;
public class AcknowledgedExtensionTest { public class AcknowledgedExtensionTest {
@ -39,9 +39,10 @@ public class AcknowledgedExtensionTest {
@Test @Test
public void checkDisplayedExtension() throws Exception { public void checkDisplayedExtension() throws Exception {
Message message = new Message(JidCreate.from("northumberland@shakespeare.lit/westminster")); Message message = StanzaBuilder.buildMessage("message-2")
message.setStanzaId("message-2"); .to("northumberland@shakespeare.lit/westminster")
message.addExtension(new ChatMarkersElements.AcknowledgedExtension("message-1")); .addExtension(new ChatMarkersElements.AcknowledgedExtension("message-1"))
.build();
assertEquals(acknowledgedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertEquals(acknowledgedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -28,7 +29,6 @@ import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements.Displaye
import org.jivesoftware.smackx.chat_markers.provider.DisplayedProvider; import org.jivesoftware.smackx.chat_markers.provider.DisplayedProvider;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.jxmpp.jid.impl.JidCreate;
public class DisplayedExtensionTest { public class DisplayedExtensionTest {
@ -39,9 +39,10 @@ public class DisplayedExtensionTest {
@Test @Test
public void checkDisplayedExtension() throws Exception { public void checkDisplayedExtension() throws Exception {
Message message = new Message(JidCreate.from("northumberland@shakespeare.lit/westminster")); Message message = StanzaBuilder.buildMessage("message-2")
message.setStanzaId("message-2"); .to("northumberland@shakespeare.lit/westminster")
message.addExtension(new ChatMarkersElements.DisplayedExtension("message-1")); .addExtension(new ChatMarkersElements.DisplayedExtension("message-1"))
.build();
assertEquals(displayedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertEquals(displayedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -39,10 +40,12 @@ public class MarkableExtensionTest {
@Test @Test
public void checkMarkableExtension() throws Exception { public void checkMarkableExtension() throws Exception {
Message message = new Message(JidCreate.from("ingrichard@royalty.england.lit/throne")); Message message = StanzaBuilder.buildMessage("message-1")
message.setStanzaId("message-1"); .to(JidCreate.from("ingrichard@royalty.england.lit/throne"))
message.setBody("My lord, dispatch; read o'er these articles."); .setBody("My lord, dispatch; read o'er these articles.")
message.addExtension(ChatMarkersElements.MarkableExtension.INSTANCE); .addExtension(ChatMarkersElements.MarkableExtension.INSTANCE)
.build();
assertEquals(markableMessageStanza, message.toXML().toString()); assertEquals(markableMessageStanza, message.toXML().toString());
} }

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -28,7 +29,6 @@ import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements.Received
import org.jivesoftware.smackx.chat_markers.provider.ReceivedProvider; import org.jivesoftware.smackx.chat_markers.provider.ReceivedProvider;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.jxmpp.jid.impl.JidCreate;
public class ReceivedExtensionTest { public class ReceivedExtensionTest {
@ -39,9 +39,10 @@ public class ReceivedExtensionTest {
@Test @Test
public void checkReceivedExtension() throws Exception { public void checkReceivedExtension() throws Exception {
Message message = new Message(JidCreate.from("northumberland@shakespeare.lit/westminster")); Message message = StanzaBuilder.buildMessage("message-2")
message.setStanzaId("message-2"); .to("northumberland@shakespeare.lit/westminster")
message.addExtension(new ChatMarkersElements.ReceivedExtension("message-1")); .addExtension(new ChatMarkersElements.ReceivedExtension("message-1"))
.build();
assertEquals(receivedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertEquals(receivedMessageStanza, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.colors;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.colors.ConsistentColor.Deficiency; import org.jivesoftware.smackx.colors.ConsistentColor.Deficiency;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View file

@ -25,6 +25,8 @@ import java.util.List;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
@ -35,7 +37,7 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
@Test @Test
public void addToMessageTest() { public void addToMessageTest() {
Message message = new Message(); Message message = StanzaBuilder.buildMessage().build();
// Check inital state (no elements) // Check inital state (no elements)
assertNull(ExplicitMessageEncryptionElement.from(message)); assertNull(ExplicitMessageEncryptionElement.from(message));
@ -45,9 +47,12 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
List<ExtensionElement> extensions = message.getExtensions(); List<ExtensionElement> extensions = message.getExtensions();
assertEquals(0, extensions.size()); assertEquals(0, extensions.size());
MessageBuilder messageBuilder = StanzaBuilder.buildMessage();
// Add OMEMO // Add OMEMO
ExplicitMessageEncryptionElement.set(message, ExplicitMessageEncryptionElement.set(messageBuilder,
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl); ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl);
message = messageBuilder.build();
extensions = message.getExtensions(); extensions = message.getExtensions();
assertEquals(1, extensions.size()); assertEquals(1, extensions.size());
assertTrue(ExplicitMessageEncryptionElement.hasProtocol(message, assertTrue(ExplicitMessageEncryptionElement.hasProtocol(message,
@ -59,8 +64,10 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
assertFalse(ExplicitMessageEncryptionElement.hasProtocol(message, assertFalse(ExplicitMessageEncryptionElement.hasProtocol(message,
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0.getNamespace())); ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0.getNamespace()));
ExplicitMessageEncryptionElement.set(message, ExplicitMessageEncryptionElement.set(messageBuilder,
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0); ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0);
message = messageBuilder.build();
extensions = message.getExtensions(); extensions = message.getExtensions();
assertEquals(2, extensions.size()); assertEquals(2, extensions.size());
assertTrue(ExplicitMessageEncryptionElement.hasProtocol(message, assertTrue(ExplicitMessageEncryptionElement.hasProtocol(message,
@ -69,9 +76,10 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl)); ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
// Check, if adding additional OMEMO wont add another element // Check, if adding additional OMEMO wont add another element
ExplicitMessageEncryptionElement.set(message, ExplicitMessageEncryptionElement.set(messageBuilder,
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl); ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl);
message = messageBuilder.build();
extensions = message.getExtensions(); extensions = message.getExtensions();
assertEquals(2, extensions.size()); assertEquals(2, extensions.size());
} }

View file

@ -25,6 +25,7 @@ import java.util.TimeZone;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.delay.packet.DelayInformation; import org.jivesoftware.smackx.delay.packet.DelayInformation;
@ -62,21 +63,21 @@ public class QueryArchiveTest extends MamTest {
@Test @Test
public void checkMamQueryResults() throws Exception { public void checkMamQueryResults() throws Exception {
Message message = new Message(); Message message = StanzaBuilder.buildMessage("iasd207")
message.setStanzaId("iasd207"); .from("coven@chat.shakespeare.lit")
message.setFrom(JidCreate.from("coven@chat.shakespeare.lit")); .to("hag66@shakespeare.lit/pda")
message.setTo(JidCreate.from("hag66@shakespeare.lit/pda")); .build();
GregorianCalendar calendar = new GregorianCalendar(2002, 10 - 1, 13, 23, 58, 37); GregorianCalendar calendar = new GregorianCalendar(2002, 10 - 1, 13, 23, 58, 37);
calendar.setTimeZone(TimeZone.getTimeZone("UTC")); calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = calendar.getTime(); Date date = calendar.getTime();
DelayInformation delay = new DelayInformation(date); DelayInformation delay = new DelayInformation(date);
Message forwardedMessage = new Message(); Message forwardedMessage = StanzaBuilder.buildMessage("162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2")
forwardedMessage.setFrom(JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); .from(JidCreate.from("coven@chat.shakespeare.lit/firstwitch"))
forwardedMessage.setStanzaId("162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); .ofType(Type.chat)
forwardedMessage.setType(Type.chat); .setBody("Thrice the brinded cat hath mew.")
forwardedMessage.setBody("Thrice the brinded cat hath mew."); .build();
Forwarded forwarded = new Forwarded(delay, forwardedMessage); Forwarded forwarded = new Forwarded(delay, forwardedMessage);

Some files were not shown because too many files have changed in this diff Show more