mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-30 07:52:06 +01:00
Start implementing first MIX discovery methods
This commit is contained in:
parent
e6a1cb2f87
commit
da7d2e7e08
10 changed files with 307 additions and 2 deletions
|
@ -2,4 +2,8 @@ package org.jivesoftware.smackx.mix.core;
|
|||
|
||||
public class MixChannel {
|
||||
|
||||
|
||||
public MixChannel(/*EntityBareJid mixChannelAddress, DiscoverInfo channelInfo*/) {
|
||||
//this.identityName = channelInfo.getIdentities("conference", "mix").get(0).getName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,39 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.mix.core;
|
||||
|
||||
import static org.jivesoftware.smackx.mix.core.MixCoreConstants.FEATURE_CORE_1;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||
import org.jivesoftware.smack.Manager;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
||||
import org.jivesoftware.smackx.mix.core.exception.NotAMixChannelOrNoPermissionToSubscribeException;
|
||||
import org.jivesoftware.smackx.mix.core.exception.NotAMixServiceException;
|
||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
||||
|
||||
import org.jxmpp.jid.DomainBareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.util.cache.ExpirationCache;
|
||||
|
||||
public final class MixManager extends Manager {
|
||||
|
||||
private static final Map<XMPPConnection, MixManager> INSTANCES = new WeakHashMap<>();
|
||||
private static final ExpirationCache<DomainBareJid, MixService> KNOWN_MIX_SERVICES =
|
||||
new ExpirationCache<>(100, 1000 * 60 * 60 * 24);
|
||||
|
||||
static {
|
||||
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||
|
@ -38,6 +59,8 @@ public final class MixManager extends Manager {
|
|||
});
|
||||
}
|
||||
|
||||
private final ServiceDiscoveryManager serviceDiscoveryManager;
|
||||
|
||||
public static MixManager getInstanceFor(XMPPConnection connection) {
|
||||
MixManager manager = INSTANCES.get(connection);
|
||||
if (manager == null) {
|
||||
|
@ -49,6 +72,107 @@ public final class MixManager extends Manager {
|
|||
|
||||
private MixManager(XMPPConnection connection) {
|
||||
super(connection);
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(MixCoreConstants.FEATURE_CORE_1);
|
||||
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||
serviceDiscoveryManager.addFeature(FEATURE_CORE_1);
|
||||
}
|
||||
|
||||
public List<EntityBareJid> discoverMixChannels(DomainBareJid mixServiceAddress)
|
||||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException, NotAMixServiceException,
|
||||
SmackException.FeatureNotSupportedException {
|
||||
MixService mixService = getOrDiscoverMixService(mixServiceAddress);
|
||||
|
||||
if (!mixService.isSearchable()) {
|
||||
throw new SmackException.FeatureNotSupportedException(MixCoreConstants.FEATURE_SEARCHABLE_1, mixServiceAddress);
|
||||
}
|
||||
|
||||
DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(mixServiceAddress);
|
||||
List<EntityBareJid> channelJids = discoverItems.getItems().stream()
|
||||
.map(DiscoverItems.Item::getEntityID)
|
||||
.map(Jid::asEntityBareJidOrThrow)
|
||||
.collect(Collectors.toList());
|
||||
return channelJids;
|
||||
}
|
||||
|
||||
private MixService getOrDiscoverMixService(DomainBareJid mixServiceAddress)
|
||||
throws SmackException.NoResponseException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException, NotAMixServiceException {
|
||||
if (KNOWN_MIX_SERVICES.containsKey(mixServiceAddress)) {
|
||||
return KNOWN_MIX_SERVICES.get(mixServiceAddress);
|
||||
}
|
||||
return discoverMixService(mixServiceAddress);
|
||||
}
|
||||
|
||||
private MixService discoverMixService(DomainBareJid mixServiceAddress)
|
||||
throws SmackException.NoResponseException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException, NotAMixServiceException {
|
||||
DiscoverInfo mixServiceDiscoverInfo = serviceDiscoveryManager.discoverInfo(mixServiceAddress);
|
||||
|
||||
if (!mixServiceDiscoverInfo.hasIdentity("conference", "mix")) {
|
||||
throw new NotAMixServiceException("Identity of the service MUST have a category 'conference' and a type of 'mix'.");
|
||||
}
|
||||
if (!mixServiceDiscoverInfo.containsFeature(FEATURE_CORE_1)) {
|
||||
throw new NotAMixServiceException("Service MUST include the '" + FEATURE_CORE_1 + "' feature.");
|
||||
}
|
||||
|
||||
mixServiceStandardComplianceChecks(mixServiceDiscoverInfo);
|
||||
|
||||
MixService mixService = new MixService(mixServiceAddress, mixServiceDiscoverInfo);
|
||||
|
||||
KNOWN_MIX_SERVICES.put(mixServiceAddress, mixService);
|
||||
|
||||
return mixService;
|
||||
}
|
||||
|
||||
public MixChannel discoverMixChannelInformation(EntityBareJid mixChannelAddress)
|
||||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException, NotAMixChannelOrNoPermissionToSubscribeException {
|
||||
DiscoverInfo channelInfo = serviceDiscoveryManager.discoverInfo(mixChannelAddress);
|
||||
|
||||
if (!channelInfo.hasIdentity("conference", "mix")) {
|
||||
throw new NotAMixChannelOrNoPermissionToSubscribeException(
|
||||
"DiscoverInfo did not contain identity with category 'conference' and type 'mix'.");
|
||||
}
|
||||
|
||||
mixChannelStandardComplianceChecks(channelInfo);
|
||||
|
||||
//return new MixChannel(mixChannelAddress, channelInfo);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform checks for standards compliance on the service.
|
||||
* These checks are not strictly necessary, but may be used to identify faulty server implementations.
|
||||
*
|
||||
* @param info DiscoverInfo of the mix service.
|
||||
*/
|
||||
private void mixServiceStandardComplianceChecks(DiscoverInfo info) {
|
||||
if (info.containsFeature(MamElements.NAMESPACE)) {
|
||||
// XEP-0369: §6.1
|
||||
throw new AssertionError("A MIX service MUST NOT advertise support for MAM.");
|
||||
}
|
||||
if (info.containsFeature(PubSub.NAMESPACE)) {
|
||||
// XEP-0369: §6.1
|
||||
throw new AssertionError("A MIX service MUST NOT advertise support for PubSub.");
|
||||
}
|
||||
}
|
||||
|
||||
private void mixChannelStandardComplianceChecks(DiscoverInfo channelInfo) throws NotAMixChannelOrNoPermissionToSubscribeException {
|
||||
if (!channelInfo.containsFeature(FEATURE_CORE_1)) {
|
||||
throw new NotAMixChannelOrNoPermissionToSubscribeException(
|
||||
"MIX channel did not advertise feature '" + FEATURE_CORE_1 + "'.");
|
||||
}
|
||||
if (!channelInfo.containsFeature(MamElements.NAMESPACE)) {
|
||||
throw new NotAMixChannelOrNoPermissionToSubscribeException(
|
||||
"MIX channel did not advertise feature '" + MamElements.NAMESPACE + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
public MixChannel join(EntityBareJid channelAddress, String nick)
|
||||
throws XMPPException.XMPPErrorException, NotAMixChannelOrNoPermissionToSubscribeException,
|
||||
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||
MixChannel channel = discoverMixChannelInformation(channelAddress);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
package org.jivesoftware.smackx.mix.core;
|
||||
|
||||
import org.jivesoftware.smackx.mix.core.element.SubscribeElement;
|
||||
import org.jivesoftware.smackx.mix.presence.MixPresenceConstants;
|
||||
|
||||
public class MixNodes {
|
||||
public static final SubscribeElement NODE_MESSAGES = new SubscribeElement(MixCoreConstants.NODE_MESSAGES);
|
||||
public static final SubscribeElement NODE_PRESENCE = new SubscribeElement(MixCoreConstants.NODE_PRESENCE);
|
||||
public static final SubscribeElement NODE_PRESENCE = new SubscribeElement(MixPresenceConstants.NODE_PRESENCE);
|
||||
public static final SubscribeElement NODE_PARTICIPANTS = new SubscribeElement(MixCoreConstants.NODE_PARTICIPANTS);
|
||||
public static final SubscribeElement NODE_INFO = new SubscribeElement(MixCoreConstants.NODE_INFO);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.jivesoftware.smackx.mix.core;
|
||||
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||
|
||||
import org.jxmpp.jid.DomainBareJid;
|
||||
|
||||
public class MixService {
|
||||
|
||||
private final DomainBareJid serviceJid;
|
||||
|
||||
private boolean isSearchable;
|
||||
|
||||
private boolean isSupportingChannelCreation;
|
||||
|
||||
public MixService(DomainBareJid serviceJid, DiscoverInfo serviceInfo) {
|
||||
this.serviceJid = serviceJid;
|
||||
|
||||
this.isSearchable = discoverIsSearchable(serviceInfo);
|
||||
this.isSupportingChannelCreation = discoverIsSupportingChannelCreation(serviceInfo);
|
||||
}
|
||||
|
||||
public boolean isSearchable() {
|
||||
return isSearchable;
|
||||
}
|
||||
|
||||
public boolean isSupportingChannelCreation() {
|
||||
return isSupportingChannelCreation;
|
||||
}
|
||||
|
||||
private boolean discoverIsSupportingChannelCreation(DiscoverInfo serviceInfo) {
|
||||
return serviceInfo.containsFeature(MixCoreConstants.FEATURE_CREATE_CHANNEL_1);
|
||||
}
|
||||
|
||||
private boolean discoverIsSearchable(DiscoverInfo serviceInfo) {
|
||||
return serviceInfo.containsFeature(MixCoreConstants.FEATURE_SEARCHABLE_1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.jivesoftware.smackx.mix.core.exception;
|
||||
|
||||
public class NotAMixChannelOrNoPermissionToSubscribeException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotAMixChannelOrNoPermissionToSubscribeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.jivesoftware.smackx.mix.core.exception;
|
||||
|
||||
public class NotAMixServiceException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotAMixServiceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.jivesoftware.smackx.mix.misc;
|
||||
|
||||
import static org.jivesoftware.smackx.mix.core.MixCoreConstants.URN_XMPP_MIX;
|
||||
|
||||
public class MixMiscConstants {
|
||||
|
||||
public static final String URN_XMPP_MIX_MISC = URN_XMPP_MIX + ":misc";
|
||||
|
||||
public static final String URN_XMPP_MIX_MISC_0 = URN_XMPP_MIX_MISC + ":0";
|
||||
|
||||
public static final String NAMESPACE_MISC_0 = URN_XMPP_MIX_MISC_0;
|
||||
|
||||
public static final String NAMESPACE_MISC = NAMESPACE_MISC_0;
|
||||
|
||||
public static final String FEATURE_NICK_REGISTER_0 = NAMESPACE_MISC_0 + "#nick-register";
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.jivesoftware.smackx.mix.misc.element;
|
||||
|
||||
import static org.jivesoftware.smackx.mix.misc.MixMiscConstants.NAMESPACE_MISC_0;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
import org.jivesoftware.smackx.mix.core.element.NickElement;
|
||||
|
||||
public abstract class RegisterElement implements ExtensionElement {
|
||||
|
||||
public static final String ELEMENT = "register";
|
||||
|
||||
private final NickElement nick;
|
||||
|
||||
public RegisterElement(NickElement nickElement) {
|
||||
this.nick = Objects.requireNonNull(nickElement, "Nick element MUST NOT be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
return new XmlStringBuilder(this)
|
||||
.rightAngleBracket()
|
||||
.append(nick)
|
||||
.closeElement(this);
|
||||
}
|
||||
|
||||
public static class V0 extends RegisterElement {
|
||||
|
||||
public V0(NickElement nickElement) {
|
||||
super(nickElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE_MISC_0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.jivesoftware.smackx.mix.misc.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
import org.jivesoftware.smackx.mix.core.element.NickElement;
|
||||
import org.jivesoftware.smackx.mix.core.element.SetNickElement;
|
||||
import org.jivesoftware.smackx.mix.misc.element.RegisterElement;
|
||||
|
||||
public abstract class RegisterElementProvider<E extends RegisterElement> extends ExtensionElementProvider<E> {
|
||||
|
||||
public static class V0 extends RegisterElementProvider<RegisterElement.V0> {
|
||||
|
||||
@Override
|
||||
public RegisterElement.V0 parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
||||
throws XmlPullParserException, IOException, SmackParsingException {
|
||||
NickElement nickElement = null;
|
||||
while (true) {
|
||||
XmlPullParser.TagEvent tagEvent = parser.nextTag();
|
||||
String name = parser.getName();
|
||||
if (tagEvent == XmlPullParser.TagEvent.START_ELEMENT) {
|
||||
if (NickElement.ELEMENT.equals(name)) {
|
||||
nickElement = new NickElement(parser.nextText());
|
||||
}
|
||||
} else if (tagEvent == XmlPullParser.TagEvent.END_ELEMENT) {
|
||||
if (SetNickElement.ELEMENT.equals(name)) {
|
||||
return new RegisterElement.V0(nickElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.jivesoftware.smackx.mix.misc.element;
|
||||
|
||||
import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
|
||||
|
||||
import org.jivesoftware.smackx.mix.core.element.NickElement;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class RegisterElementTest {
|
||||
|
||||
@Test
|
||||
public void v0testSerialization() {
|
||||
RegisterElement register = new RegisterElement.V0(new NickElement("thirdwitch"));
|
||||
String expectedXml = "" +
|
||||
"<register xmlns='urn:xmpp:mix:misc:0'>\n" +
|
||||
" <nick>thirdwitch</nick>\n" +
|
||||
"</register>\n";
|
||||
|
||||
assertXmlSimilar(expectedXml, register.toXML());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue