1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-22 20:12:07 +01:00

Compare commits

..

10 commits

Author SHA1 Message Date
Florian Schmaus
926c5892ad Fix 260c5539b ("Add support for XEP-0118: UserTune")
Fix a bug in the EqualsBuilder usage in UserTuneElement. Properly
synchronize listeners using CopyOnWriteSet. Make methods and fields
static where sensible and possible. Make
disableUserTuneNotifications() public (why was it private?). And a few
other minor fixes.
2019-10-17 12:27:33 +02:00
adiaholic
260c5539b5 Add support for XEP-0118: UserTune
This commit will enable user to communicate
information about music to which user is listening.
This feature is less of a requirement and more like fun to me.
An attempt at solving SMACK-257.

Incase you see any chances of improvement,
please let me know :)
2019-10-17 12:24:30 +02:00
Florian Schmaus
8f371c5381 Fix datatype.Scalar equals() and hashCode() methods
The previously used Number.equals() and hashCode() was just delegated
to Object and hence did not behave as expected.
2019-10-17 12:24:30 +02:00
Florian Schmaus
db150a850a Fix order of 'actual' and 'expected' in XmlUnitUtils 2019-10-16 19:35:22 +02:00
John Haubrich
7a58862794 gradle: Ensure git command is run projectDir rather than CWD.
The assert on line 659 was causing my build to fail. Two issues caused
gitCommit to be empty.

1. The cmd 'git describe --always --tags --dirty=+' was not given
   enough time to complete and had not exited which meant no text in
   proc.text
2. The two git commands on lines 653 and 658 were run from the
   CWD of my Eclipse IDE, not the $projectDir which caused git to return
   an error 128.

To solve the two issues I added a waitForOrKill method call to
proc (like the srCmd had) and I set the execute to run in $projectDir
which I think was the intent/assumption in the original code.

Also add waitFor on git describe command.
2019-10-12 10:34:05 +02:00
Florian Schmaus
b510d373b5 reactor: have synchronized block include peeking at scheduled actions
If we do not peek at the scheduled actions in the reactors
synchronized block, then there is a kind of lost-update problem. While
Ractor.schedule() will call wakeup() on the selector, a thread could
have already determined the value of selectWait, while being blocked
at the start of the synchronized reactor section. Once it is able to
enter the section, it will use an outdated selectWait value.

This leads to scheduled actions not being executed on time.

Thanks to Eng ChongMeng for reporting this and suggesting the fix.
2019-10-12 10:22:31 +02:00
Florian Schmaus
05c920ab56 Fix typo in comment 2019-10-12 10:14:17 +02:00
Florian Schmaus
e23babf147 Add Stanza.setNewStanzaId() and ensureStanzaIdSet()
Also deprecate setStanzaId() since it was not clear if this would
create a new stanza ID or just ensure that one is set.
2019-10-05 23:16:15 +02:00
Florian Schmaus
133ee29150 Add XmlStringBuilderTest 2019-10-05 23:01:03 +02:00
Florian Schmaus
30bbdf9fdb Remove deprecated methods from Stanza class 2019-10-05 23:00:57 +02:00
26 changed files with 1031 additions and 115 deletions

View file

@ -650,13 +650,16 @@ def getGitCommit() {
def dotGit = new File("$projectDir/.git")
if (!dotGit.isDirectory()) return 'non-git build'
def projectDir = dotGit.getParentFile()
def cmd = 'git describe --always --tags --dirty=+'
def proc = cmd.execute()
def proc = cmd.execute(null, projectDir)
proc.waitForOrKill(10 * 1000)
def gitCommit = proc.text.trim()
assert !gitCommit.isEmpty()
def srCmd = 'git symbolic-ref --short HEAD'
def srProc = srCmd.execute()
def srProc = srCmd.execute(null, projectDir)
srProc.waitForOrKill(10 * 1000)
if (srProc.exitValue() == 0) {
// Only add the information if the git command was

View file

@ -56,6 +56,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| [SI File Transfer](filetransfer.md) | [XEP-0096](https://xmpp.org/extensions/xep-0096.html) | n/a | Transfer files between two users over XMPP. |
| User Mood | [XEP-0107](https://xmpp.org/extensions/xep-0107.html) | 1.2.1 | Communicate the users current mood. |
| [Entity Capabilities](caps.md) | [XEP-0115](https://xmpp.org/extensions/xep-0115.html) | n/a | Broadcasting and dynamic discovery of entity capabilities. |
| User Tune | [XEP-0118](https://xmpp.org/extensions/xep-0118.html) | n/a | Defines a payload format for communicating information about music to which a user is listening. |
| Data Forms Validation | [XEP-0122](https://xmpp.org/extensions/xep-0122.html) | n/a | Enables an application to specify additional validation guidelines . |
| Service Administration | [XEP-0133](https://xmpp.org/extensions/xep-0133.html) | n/a | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. |
| Stream Compression | [XEP-0138](https://xmpp.org/extensions/xep-0138.html) | n/a | Support for optional compression of the XMPP stream.

View file

@ -204,6 +204,9 @@ public class SmackReactor {
return;
}
int newSelectedKeysCount = 0;
List<SelectionKey> selectedKeys;
synchronized (selector) {
ScheduledAction nextScheduledAction = scheduledActions.peek();
long selectWait;
@ -216,13 +219,10 @@ public class SmackReactor {
}
if (selectWait < 0) {
// A scheduled action was just released and become ready to execute.
// A scheduled action was just released and became ready to execute.
return;
}
int newSelectedKeysCount = 0;
List<SelectionKey> selectedKeys;
synchronized (selector) {
// Before we call select, we handle the pending the interest Ops. This will not block since no other
// thread is currently in select() at this time.
// Note: This was put deliberately before the registration lock. It may cause more synchronization but

View file

@ -54,13 +54,29 @@ public abstract class Scalar extends java.lang.Number {
}
@Override
public final int hashCode() {
return number.hashCode();
}
public abstract int hashCode();
@Override
public final boolean equals(Object other) {
return number.equals(other);
public boolean equals(Object other) {
if (!(other instanceof Scalar)) {
return false;
}
Scalar otherScalar = (Scalar) other;
if (longValue() == otherScalar.longValue()) {
return true;
}
if (doubleValue() == otherScalar.doubleValue()) {
return true;
}
if (floatValue() == otherScalar.floatValue()) {
return true;
}
return false;
}
@Override

View file

@ -39,4 +39,19 @@ public final class UInt16 extends Scalar {
public static UInt16 from(int number) {
return new UInt16(number);
}
@Override
public int hashCode() {
return number;
}
@Override
public boolean equals(Object other) {
if (other instanceof UInt16) {
UInt16 otherUint16 = (UInt16) other;
return number == otherUint16.number;
}
return super.equals(other);
}
}

View file

@ -39,4 +39,20 @@ public final class UInt32 extends Scalar {
public static UInt32 from(long number) {
return new UInt32(number);
}
@Override
public int hashCode() {
// TODO: Use Long.hashCode(number) once Smack's minimum Android SDK level is 24 or higher.
return (int) (number ^ (number >>> 32));
}
@Override
public boolean equals(Object other) {
if (other instanceof UInt32) {
UInt32 otherUint32 = (UInt32) other;
return number == otherUint32.number;
}
return super.equals(other);
}
}

View file

@ -31,8 +31,6 @@ import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
/**
* Base class for XMPP Stanzas, which are called Stanza in older versions of Smack (i.e. &lt; 4.1).
@ -47,6 +45,7 @@ import org.jxmpp.stringprep.XmppStringprepException;
* </p>
*
* @author Matt Tucker
* @author Florian Schmaus
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#stanzas">RFC 6120 § 8. XML Stanzas</a>
*/
public abstract class Stanza implements TopLevelStreamElement {
@ -109,16 +108,6 @@ public abstract class Stanza implements TopLevelStreamElement {
return id;
}
/**
* Get the Stanza ID.
* @return the stanza id.
* @deprecated use {@link #getStanzaId()} instead.
*/
@Deprecated
public String getPacketID() {
return getStanzaId();
}
/**
* Sets the unique ID of the packet. To indicate that a stanza has no id
* pass <code>null</code> as the packet's id value.
@ -132,16 +121,6 @@ public abstract class Stanza implements TopLevelStreamElement {
this.id = id;
}
/**
* Set the stanza ID.
* @param packetID TODO javadoc me please
* @deprecated use {@link #setStanzaId(String)} instead.
*/
@Deprecated
public void setPacketID(String packetID) {
setStanzaId(packetID);
}
/**
* Check if this stanza has an ID set.
*
@ -159,9 +138,43 @@ public abstract class Stanza implements TopLevelStreamElement {
*
* @return the stanza id.
* @since 4.2
* @deprecated use {@link #setNewStanzaId()} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public String setStanzaId() {
if (!hasStanzaIdSet()) {
return ensureStanzaIdSet();
}
/**
* 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();
@ -179,26 +192,6 @@ public abstract class Stanza implements TopLevelStreamElement {
return 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.
* @throws IllegalArgumentException if to is not a valid JID String.
* @deprecated use {@link #setTo(Jid)} instead.
*/
@Deprecated
public void setTo(String to) {
Jid jid;
try {
jid = JidCreate.from(to);
}
catch (XmppStringprepException e) {
throw new IllegalArgumentException(e);
}
setTo(jid);
}
/**
* Sets who the packet is being sent "to". The XMPP protocol often makes
* the "to" attribute optional, so it does not always need to be set.
@ -221,27 +214,6 @@ public abstract class Stanza implements TopLevelStreamElement {
return 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 to.
* @throws IllegalArgumentException if from is not a valid JID String.
* @deprecated use {@link #setFrom(Jid)} instead.
*/
@Deprecated
public void setFrom(String from) {
Jid jid;
try {
jid = JidCreate.from(from);
}
catch (XmppStringprepException e) {
throw new IllegalArgumentException(e);
}
setFrom(jid);
}
/**
* Sets who the packet is being sent "from". The XMPP protocol often
* makes the "from" attribute optional, so it does not always need to
@ -263,17 +235,6 @@ public abstract class Stanza implements TopLevelStreamElement {
return error;
}
/**
* Sets the error for this packet.
*
* @param error the error to associate with this packet.
* @deprecated use {@link #setError(org.jivesoftware.smack.packet.StanzaError.Builder)} instead.
*/
@Deprecated
public void setError(StanzaError error) {
this.error = error;
}
/**
* Sets the error for this stanza.
*

View file

@ -43,7 +43,7 @@ public class XmlUnitUtils {
NormalizedSource expected = new NormalizedSource(new StreamSource(new StringReader(expectedString)));
NormalizedSource actual = new NormalizedSource(new StreamSource(new StringReader(actualString)));
return XmlAssert.assertThat(expected).and(actual)
return XmlAssert.assertThat(actual).and(expected)
.ignoreChildNodesOrder()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAllAttributes, ElementSelectors.byNameAndText))
.normalizeWhitespace();

View file

@ -0,0 +1,47 @@
/**
*
* 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 org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.test.util.XmlUnitUtils;
import org.junit.jupiter.api.Test;
public class XmlStringBuilderTest {
/**
* Test that {@link XmlStringBuilder} does not omit the second inner namespace declaration.
*/
@Test
public void equalInnerNamespaceTest() {
StandardExtensionElement innerOne = StandardExtensionElement.builder("inner", "inner-namespace").build();
StandardExtensionElement innerTwo = StandardExtensionElement.builder("inner", "inner-namespace").build();
StandardExtensionElement outer = StandardExtensionElement.builder("outer", "outer-namespace").addElement(
innerOne).addElement(innerTwo).build();
String expectedXml = "<outer xmlns='outer-namespace'><inner xmlns='inner-namespace'></inner><inner xmlns='inner-namespace'></inner></outer>";
XmlStringBuilder actualXml = outer.toXML(XmlEnvironment.EMPTY);
XmlUnitUtils.assertXmlSimilar(expectedXml, actualXml);
StringBuilder actualXmlTwo = actualXml.toXML(XmlEnvironment.EMPTY);
XmlUnitUtils.assertXmlSimilar(expectedXml, actualXmlTwo);
}
}

View file

@ -22,7 +22,6 @@ import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.packet.id.StanzaIdUtil;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -83,9 +82,7 @@ public class DeliveryReceiptRequest implements ExtensionElement {
* @return the Message ID which will be used as receipt ID
*/
public static String addTo(Message message) {
if (message.getStanzaId() == null) {
message.setStanzaId(StanzaIdUtil.newStanzaId());
}
message.ensureStanzaIdSet();
message.addExtension(new DeliveryReceiptRequest());
return message.getStanzaId();
}

View file

@ -0,0 +1,27 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
import org.jxmpp.jid.BareJid;
public interface UserTuneListener {
void onUserTuneUpdated(BareJid jid, Message message, UserTuneElement userTuneElement);
}

View file

@ -0,0 +1,143 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jivesoftware.smack.AsyncButOrdered;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.pep.PepListener;
import org.jivesoftware.smackx.pep.PepManager;
import org.jivesoftware.smackx.pubsub.EventElement;
import org.jivesoftware.smackx.pubsub.ItemsExtension;
import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
/**
* Entry point for Smacks API for XEP-0118: User Tune.
* <br>
* To publish a UserTune, please use {@link #publishUserTune(UserTuneElement)} method. This will publish the node.
* <br>
* To stop publishing a UserTune, please use {@link #clearUserTune()} method. This will send a disabling publish signal.
* <br>
* To add a UserTune listener in order to remain updated with other users UserTune, use {@link #addUserTuneListener(UserTuneListener)} method.
* <br>
* To link a UserTuneElement with {@link Message}, use 'message.addExtension(userTuneElement)'.
* <br>
* An example to illustrate is provided inside UserTuneElementTest inside the test package.
* <br>
* @see <a href="https://xmpp.org/extensions/xep-0118.html">
* XEP-0118: User Tune</a>
*/
public final class UserTuneManager extends Manager {
public static final String USERTUNE_NODE = "http://jabber.org/protocol/tune";
public static final String USERTUNE_NOTIFY = USERTUNE_NODE + "+notify";
private static final Map<XMPPConnection, UserTuneManager> INSTANCES = new WeakHashMap<>();
private static boolean ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT = true;
private final Set<UserTuneListener> userTuneListeners = new CopyOnWriteArraySet<>();
private final AsyncButOrdered<BareJid> asyncButOrdered = new AsyncButOrdered<>();
private final ServiceDiscoveryManager serviceDiscoveryManager;
private final PepManager pepManager;
public static synchronized UserTuneManager getInstanceFor(XMPPConnection connection) throws NotLoggedInException {
UserTuneManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new UserTuneManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private UserTuneManager(XMPPConnection connection) throws NotLoggedInException {
super(connection);
pepManager = PepManager.getInstanceFor(connection);
pepManager.addPepListener(new PepListener() {
@Override
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
if (!USERTUNE_NODE.equals(event.getEvent().getNode())) {
return;
}
final BareJid contact = from.asBareJid();
asyncButOrdered.performAsyncButOrdered(contact, () -> {
ItemsExtension itemsExtension = (ItemsExtension) event.getEvent();
List<ExtensionElement> items = itemsExtension.getExtensions();
@SuppressWarnings("unchecked")
PayloadItem<UserTuneElement> payload = (PayloadItem<UserTuneElement>) items.get(0);
UserTuneElement tune = payload.getPayload();
for (UserTuneListener listener : userTuneListeners) {
listener.onUserTuneUpdated(contact, message, tune);
}
});
}
});
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
if (ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT) {
enableUserTuneNotifications();
}
}
public static void setUserTuneNotificationsEnabledByDefault(boolean bool) {
ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT = bool;
}
public void enableUserTuneNotifications() {
serviceDiscoveryManager.addFeature(USERTUNE_NOTIFY);
}
public void disableUserTuneNotifications() {
serviceDiscoveryManager.removeFeature(USERTUNE_NOTIFY);
}
public void clearUserTune() throws NotLoggedInException, NotALeafNodeException, NoResponseException, NotConnectedException, XMPPErrorException, InterruptedException {
publishUserTune(UserTuneElement.EMPTY_USER_TUNE);
}
public void publishUserTune(UserTuneElement userTuneElement) throws NotLoggedInException, NotALeafNodeException, NoResponseException, NotConnectedException, XMPPErrorException, InterruptedException {
// TODO: To prevent a large number of updates when a user is skipping through tracks, an implementation SHOULD wait several seconds before publishing new tune information.
pepManager.publish(USERTUNE_NODE, new PayloadItem<>(userTuneElement));
}
public boolean addUserTuneListener(UserTuneListener listener) {
return userTuneListeners.add(listener);
}
public boolean removeUserTuneListener(UserTuneListener listener) {
return userTuneListeners.remove(listener);
}
}

View file

@ -0,0 +1,266 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune.element;
import java.net.URI;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
* {@link ExtensionElement} that contains the UserTune. <br>
* Instance of UserTuneElement can be created using {@link Builder#build()}
* method.
*/
public final class UserTuneElement implements ExtensionElement {
public static final String NAMESPACE = "http://jabber.org/protocol/tune";
public static final String ELEMENT = "tune";
private final String artist;
private final UInt16 length;
private final Integer rating;
private final String source;
private final String title;
private final String track;
private final URI uri;
public static final UserTuneElement EMPTY_USER_TUNE = null;
private UserTuneElement(Builder builder) {
this.artist = builder.artist;
this.length = builder.length;
this.rating = builder.rating;
this.source = builder.source;
this.title = builder.title;
this.track = builder.track;
this.uri = builder.uri;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
public String getArtist() {
return artist;
}
public UInt16 getLength() {
return length;
}
public Integer getRating() {
return rating;
}
public String getSource() {
return source;
}
public String getTitle() {
return title;
}
public String getTrack() {
return track;
}
public URI getUri() {
return uri;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this);
if (isEmptyUserTune()) {
return xml.closeEmptyElement();
}
xml.rightAngleBracket();
xml.optElement("artist", artist);
xml.optElement("length", length);
xml.optElement("rating", rating);
xml.optElement("source", source);
xml.optElement("title", title);
xml.optElement("track", track);
xml.optElement("uri", uri);
return xml.closeElement(getElementName());
}
private boolean isEmptyUserTune() {
return this.equals(EMPTY_USER_TUNE);
}
public static boolean hasUserTuneElement(Message message) {
return message.hasExtension(ELEMENT, NAMESPACE);
}
public static UserTuneElement from(Message message) {
return message.getExtension(ELEMENT, NAMESPACE);
}
@Override
public int hashCode() {
return HashCode.builder()
.append(artist)
.append(length)
.append(rating)
.append(source)
.append(title)
.append(track)
.append(uri).build();
}
@Override
public boolean equals(Object obj) {
return EqualsUtil
.equals(this, obj, (equalsBuilder, otherTune) -> equalsBuilder
.append(artist, otherTune.artist)
.append(length, otherTune.length)
.append(rating, otherTune.rating)
.append(source, otherTune.source)
.append(title, otherTune.title)
.append(track, otherTune.track)
.append(uri, otherTune.uri));
}
/**
* Returns a new instance of {@link Builder}.
* @return Builder
*/
public static Builder getBuilder() {
return new Builder();
}
/**
* This class defines a Builder class for {@link UserTuneElement}. <br>
* {@link UserTuneElement} instance can be obtained using the {@link #build()} method as follows. <br>
* UserTuneElement.Builder builder = new UserTuneElement.Builder();
* builder.setSource("Yessongs"); builder.setTitle("Heart of the Sunrise");
* UserTuneElement userTuneElement = builder.build(); <br>
* Values such as title, source, artist, length, source, track and uri can be set using their respective setters through {@link Builder}
*/
public static final class Builder {
private String artist;
private UInt16 length;
private Integer rating;
private String source;
private String title;
private String track;
private URI uri;
private Builder() {
}
/**
* Artist is an optional element in UserTuneElement.
* @param artist.
* @return builder.
*/
public Builder setArtist(String artist) {
this.artist = artist;
return this;
}
/**
* Length is an optional element in UserTuneElement.
* @param length.
* @return builder.
*/
public Builder setLength(int length) {
return setLength(UInt16.from(length));
}
/**
* Length is an optional element in UserTuneElement.
* @param length.
* @return builder.
*/
public Builder setLength(UInt16 length) {
this.length = length;
return this;
}
/**
* Rating is an optional element in UserTuneElement.
* @param rating.
* @return builder.
*/
public Builder setRating(int rating) {
this.rating = rating;
return this;
}
/**
* Source is an optional element in UserTuneElement.
* @param source.
* @return builder.
*/
public Builder setSource(String source) {
this.source = source;
return this;
}
/**
* Title is an optional element in UserTuneElement.
* @param title.
* @return builder.
*/
public Builder setTitle(String title) {
this.title = title;
return this;
}
/**
* Track is an optional element in UserTuneElement.
* @param track.
* @return builder.
*/
public Builder setTrack(String track) {
this.track = track;
return this;
}
/**
* URI is an optional element in UserTuneElement.
* @param uri.
* @return builder.
*/
public Builder setUri(URI uri) {
this.uri = uri;
return this;
}
/**
* This method is called to build a UserTuneElement.
* @return UserTuneElement.
*/
public UserTuneElement build() {
return new UserTuneElement(this);
}
}
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Aditya Borikar
*
* 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.
*/
/**
* Smack's API for <a href="https://xmpp.org/extensions/xep-0118.html"> XEP-0118: User Tune</a>.
*/
package org.jivesoftware.smackx.usertune.element;

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Aditya Borikar
*
* 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.
*/
/**
* Smack's API for <a href="https://xmpp.org/extensions/xep-0118.html"> XEP-0118: User Tune</a>.
*/
package org.jivesoftware.smackx.usertune;

View file

@ -0,0 +1,89 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune.provider;
import java.io.IOException;
import java.net.URI;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
/**
* This is the Provider Class for {@link UserTuneElement}.
*/
public class UserTuneProvider extends ExtensionElementProvider<UserTuneElement> {
public static final UserTuneProvider INSTANCE = new UserTuneProvider();
@Override
public UserTuneElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
UserTuneElement.Builder builder = UserTuneElement.getBuilder();
XmlPullParser.TagEvent tag = parser.nextTag();
outerloop: while (true) {
switch (tag) {
case START_ELEMENT:
String name = parser.getName();
String namespace = parser.getNamespace();
if (!UserTuneElement.NAMESPACE.equals(namespace)) {
continue outerloop;
}
while (tag == XmlPullParser.TagEvent.START_ELEMENT) {
switch (name) {
case "artist":
builder.setArtist(parser.nextText());
break;
case "length":
builder.setLength(ParserUtils.getIntegerFromNextText(parser));
break;
case "rating":
builder.setRating(ParserUtils.getIntegerFromNextText(parser));
break;
case "source":
builder.setSource(parser.nextText());
break;
case "title":
builder.setTitle(parser.nextText());
break;
case "track":
builder.setTrack(parser.nextText());
break;
case "uri":
URI uri = ParserUtils.getUriFromNextText(parser);
builder.setUri(uri);
break;
}
tag = parser.nextTag();
name = parser.getName();
}
break;
case END_ELEMENT:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return builder.build();
}
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Aditya Borikar
*
* 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.
*/
/**
* Smack's API for <a href="https://xmpp.org/extensions/xep-0118.html"> XEP-0118: User Tune</a>.
*/
package org.jivesoftware.smackx.usertune.provider;

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");
* you may not use this file except in compliance with the License.
@ -27,7 +27,6 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.id.StanzaIdUtil;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.vcardtemp.packet.VCard;
@ -105,7 +104,7 @@ public final class VCardManager extends Manager {
vcard.setType(IQ.Type.set);
// Also make sure to generate a new stanza id (the given vcard could be a vcard result), in which case we don't
// want to use the same stanza id again (although it wouldn't break if we did)
vcard.setStanzaId(StanzaIdUtil.newStanzaId());
vcard.setNewStanzaId();
connection().createStanzaCollectorAndSend(vcard).nextResultOrThrow();
}

View file

@ -589,4 +589,11 @@
<className>org.jivesoftware.smackx.mood.provider.MoodProvider</className>
</extensionProvider>
<!-- XEP-0118: User Tune -->
<extensionProvider>
<elementName>tune</elementName>
<namespace>http://jabber.org/protocol/tune</namespace>
<className>org.jivesoftware.smackx.usertune.provider.UserTuneProvider</className>
</extensionProvider>
</smackProviders>

View file

@ -0,0 +1,74 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;
import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.test.util.SmackTestUtil.XmlPullParserKind;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
import org.jivesoftware.smackx.usertune.provider.UserTuneProvider;
import org.junit.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
public class UserTuneElementTest extends SmackTestSuite {
private final String xml = "<tune xmlns='http://jabber.org/protocol/tune'>" +
"<artist>Yes</artist>" +
"<length>686</length>" +
"<rating>8</rating>" +
"<source>Yessongs</source>" +
"<title>Heart of the Sunrise</title>" +
"<track>3</track>" +
"<uri>http://www.yesworld.com/lyrics/Fragile.html#9</uri>" +
"</tune>";
@Test
public void toXmlTest() throws IOException, XmlPullParserException, SmackParsingException, URISyntaxException {
URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9");
UserTuneElement.Builder builder = UserTuneElement.getBuilder();
UserTuneElement userTuneElement = builder.setArtist("Yes")
.setLength(686)
.setRating(8)
.setSource("Yessongs")
.setTitle("Heart of the Sunrise")
.setTrack("3")
.setUri(uri)
.build();
assertXmlSimilar(xml, userTuneElement.toXML().toString());
}
@ParameterizedTest
@EnumSource(value = SmackTestUtil.XmlPullParserKind.class)
public void userTuneElementProviderTest(XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
XmlPullParser parser = SmackTestUtil.getParserFor(xml, parserKind);
UserTuneElement parsed = UserTuneProvider.INSTANCE.parse(parser);
assertXmlSimilar(xml, parsed.toXML().toString());
}
}

View file

@ -0,0 +1,58 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import java.net.URI;
import java.net.URISyntaxException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
import org.junit.Test;
public class UserTuneManagerTest extends SmackTestSuite{
@Test
public void addMessage() throws URISyntaxException {
UserTuneElement.Builder builder = UserTuneElement.getBuilder();
builder.setArtist("Yes");
builder.setLength(686);
builder.setRating(8);
builder.setSource("Yessongs");
builder.setTitle("Heart of the Sunrise");
builder.setTrack("3");
URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9");
builder.setUri(uri);
UserTuneElement userTuneElement = builder.build();
Message message = new Message();
message.addExtension(userTuneElement);
assertTrue(message.hasExtension(UserTuneElement.ELEMENT, UserTuneElement.NAMESPACE));
assertTrue(UserTuneElement.hasUserTuneElement(message));
UserTuneElement element = UserTuneElement.from(message);
assertNotNull(element);
assertEquals(userTuneElement, element);
}
}

View file

@ -67,7 +67,7 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest {
EntityBareJid userTwo = conTwo.getUser().asEntityBareJid();
Message message = new Message(userTwo);
String messageId = message.setStanzaId();
String messageId = message.ensureStanzaIdSet();
final String messageBody = "Test MAM message (" + testRunId + ')';
message.setBody(messageBody);

View file

@ -0,0 +1,89 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;
import java.net.URI;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.junit.AfterClass;
import org.jxmpp.jid.BareJid;
public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest {
private final UserTuneManager utm1;
private final UserTuneManager utm2;
public UserTuneIntegrationTest(SmackIntegrationTestEnvironment<?> environment) throws NotLoggedInException {
super(environment);
utm1 = UserTuneManager.getInstanceFor(conOne);
utm2 = UserTuneManager.getInstanceFor(conTwo);
}
@SmackIntegrationTest
public void test() throws Exception {
URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9");
UserTuneElement.Builder builder = UserTuneElement.getBuilder();
UserTuneElement userTuneElement1 = builder.setArtist("Yes")
.setLength(686)
.setRating(8)
.setSource("Yessongs")
.setTitle("Heart of the Sunrise")
.setTrack("3")
.setUri(uri)
.build();
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
final SimpleResultSyncPoint userTuneReceived = new SimpleResultSyncPoint();
final UserTuneListener userTuneListener = new UserTuneListener() {
@Override
public void onUserTuneUpdated(BareJid jid, Message message, UserTuneElement userTuneElement) {
if (userTuneElement.equals(userTuneElement1)) {
userTuneReceived.signal();
}
}
};
utm2.addUserTuneListener(userTuneListener);
try {
utm1.publishUserTune(userTuneElement1);
userTuneReceived.waitForResult(timeout);
} finally {
utm2.removeUserTuneListener(userTuneListener);
}
}
@AfterClass
public void unsubscribe()
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
}
}

View file

@ -0,0 +1,17 @@
/**
*
* Copyright 2019 Aditya Borikar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.usertune;

View file

@ -40,6 +40,10 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
/**
* RTPBridge IQ Stanza used to request and retrieve a RTPBridge Candidates that can be used for a Jingle Media Transmission between two parties that are behind NAT.
* This Jingle Bridge has all the needed information to establish a full UDP Channel (Send and Receive) between two parties.
@ -397,7 +401,6 @@ public class RTPBridge extends IQ {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
@SuppressWarnings("deprecation")
public static RTPBridge getRTPBridge(XMPPConnection connection, String sessionID) throws NotConnectedException, InterruptedException {
if (!connection.isConnected()) {
@ -405,7 +408,13 @@ public class RTPBridge extends IQ {
}
RTPBridge rtpPacket = new RTPBridge(sessionID);
rtpPacket.setTo(RTPBridge.NAME + "." + connection.getXMPPServiceDomain());
DomainBareJid jid;
try {
jid = JidCreate.domainBareFrom(RTPBridge.NAME + "." + connection.getXMPPServiceDomain());
} catch (XmppStringprepException e) {
throw new AssertionError(e);
}
rtpPacket.setTo(jid);
StanzaCollector collector = connection.createStanzaCollectorAndSend(rtpPacket);
@ -469,7 +478,6 @@ public class RTPBridge extends IQ {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
@SuppressWarnings("deprecation")
public static RTPBridge relaySession(XMPPConnection connection, String sessionID, String pass, TransportCandidate proxyCandidate, TransportCandidate localCandidate) throws NotConnectedException, InterruptedException {
if (!connection.isConnected()) {
@ -477,7 +485,13 @@ public class RTPBridge extends IQ {
}
RTPBridge rtpPacket = new RTPBridge(sessionID, RTPBridge.BridgeAction.change);
rtpPacket.setTo(RTPBridge.NAME + "." + connection.getXMPPServiceDomain());
DomainBareJid jid;
try {
jid = JidCreate.domainBareFrom(RTPBridge.NAME + "." + connection.getXMPPServiceDomain());
} catch (XmppStringprepException e) {
throw new AssertionError(e);
}
rtpPacket.setTo(jid);
rtpPacket.setType(Type.set);
rtpPacket.setPass(pass);
@ -506,7 +520,6 @@ public class RTPBridge extends IQ {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
@SuppressWarnings("deprecation")
public static String getPublicIP(XMPPConnection xmppConnection) throws NotConnectedException, InterruptedException {
if (!xmppConnection.isConnected()) {
@ -514,7 +527,13 @@ public class RTPBridge extends IQ {
}
RTPBridge rtpPacket = new RTPBridge(RTPBridge.BridgeAction.publicip);
rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getXMPPServiceDomain());
DomainBareJid jid;
try {
jid = JidCreate.domainBareFrom(RTPBridge.NAME + "." + xmppConnection.getXMPPServiceDomain());
} catch (XmppStringprepException e) {
throw new AssertionError(e);
}
rtpPacket.setTo(jid);
rtpPacket.setType(Type.set);
// LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort());

View file

@ -37,6 +37,10 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
/**
* STUN IQ Stanza used to request and retrieve a STUN server and port to make p2p connections easier. STUN is usually used by Jingle Media Transmission between two parties that are behind NAT.
*
@ -177,7 +181,6 @@ public class STUN extends SimpleIQ {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
@SuppressWarnings("deprecation")
public static STUN getSTUNServer(XMPPConnection connection) throws NotConnectedException, InterruptedException {
if (!connection.isConnected()) {
@ -185,7 +188,13 @@ public class STUN extends SimpleIQ {
}
STUN stunPacket = new STUN();
stunPacket.setTo(DOMAIN + "." + connection.getXMPPServiceDomain());
DomainBareJid jid;
try {
jid = JidCreate.domainBareFrom(DOMAIN + "." + connection.getXMPPServiceDomain());
} catch (XmppStringprepException e) {
throw new AssertionError(e);
}
stunPacket.setTo(jid);
StanzaCollector collector = connection.createStanzaCollectorAndSend(stunPacket);