
rocks.xmpp.im.subscription.PresenceManager Maven / Gradle / Ivy
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.im.subscription;
import rocks.xmpp.addr.Jid;
import rocks.xmpp.core.session.Manager;
import rocks.xmpp.core.session.XmppSession;
import rocks.xmpp.core.stanza.model.Presence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages subscription requests and presences.
*
* This class allows to request, approve, deny and unsubscribe subscriptions.
*
* This class is unconditionally thread-safe.
*
* @author Christian Schudt
* @see 3. Managing Presence Subscriptions
*/
public final class PresenceManager extends Manager {
// TODO auto deny or auto approve some or all requests.
private final Map> presenceMap = new ConcurrentHashMap<>();
private final Map lastSentPresences = new ConcurrentHashMap<>();
private PresenceManager(final XmppSession xmppSession) {
super(xmppSession, false);
}
@Override
protected final void initialize() {
xmppSession.addInboundPresenceListener(e -> {
Presence presence = e.getPresence();
if (presence.getFrom() != null) {
// Store the user (bare JID) in the map, associated with different resources.
Map presencesPerResource = presenceMap.computeIfAbsent(presence.getFrom().asBareJid(), key -> new ConcurrentHashMap<>());
// Update the contact's resource with the presence.
presencesPerResource.put(presence.getFrom().getResource() != null ? presence.getFrom().getResource() : "", presence);
}
});
xmppSession.addOutboundPresenceListener(e -> {
Presence presence = e.getPresence();
// Store the last sent presences, in order to automatically resend them, after a disconnect.
if (presence.isAvailable()) {
if (presence.getTo() == null) {
lastSentPresences.put("", presence);
} else {
lastSentPresences.put(presence.getTo().toString(), presence);
}
} else if (presence.getType() == Presence.Type.UNAVAILABLE) {
if (presence.getTo() == null) {
lastSentPresences.remove("");
} else {
lastSentPresences.remove(presence.getTo().toString());
}
}
});
}
/**
* Gets the presence for a given contact.
*
* If the given JID is a bare JID, and the contact has sent multiple presences with different resources,
* the "best" presence is returned, i.e. the presence with the highest priority or the presence with the "most available" {@code } element.
*
*
* If the JID is a full JID, the exact presence of that JID is returned.
*
*
* If no presence could be found an unavailable presence is returned.
*
*
* @param jid The JID.
* @return The presence.
*/
public final Presence getPresence(Jid jid) {
if (Objects.requireNonNull(jid, "jid must not be null.").isBareJid()) {
Map presencesPerResource = presenceMap.get(jid);
if (presencesPerResource != null) {
List presences = new ArrayList<>(presencesPerResource.values());
if (!presences.isEmpty()) {
presences.sort(null);
return presences.get(0);
}
}
} else {
Map presencesPerResource = presenceMap.get(jid.asBareJid());
if (presencesPerResource != null) {
Presence presence = presencesPerResource.get(jid.getResource());
if (presence != null) {
return presence;
}
}
}
Presence presence = new Presence(Presence.Type.UNAVAILABLE);
presence.setFrom(jid);
return presence;
}
/**
* Sends a subscription request to a potential contact.
*
* @param jid The contact's JID.
* @param status The status, which is used for additional information during the subscription request.
* @return The id, which is used for the request.
* @see 3.1.1. Client Generation of Outbound Subscription Request
*/
public final String requestSubscription(Jid jid, String status) {
// the value of the 'to' attribute MUST be a bare JID
Presence presence = new Presence(jid.asBareJid(), Presence.Type.SUBSCRIBE, status, UUID.randomUUID().toString());
xmppSession.send(presence);
return presence.getId();
}
/**
* Approves a subscription request.
*
* @param jid The contact's JID, who has previously requested a subscription.
* @return The id, which is used for the approval.
* @see 3.1.4. Client Processing of Inbound Subscription Request
*/
public final String approveSubscription(Jid jid) {
// For tracking purposes, a client SHOULD include an 'id' attribute in a subscription approval or subscription denial; this 'id' attribute MUST NOT mirror the 'id' attribute of the subscription request.
Presence presence = new Presence(jid, Presence.Type.SUBSCRIBED, null, UUID.randomUUID().toString());
xmppSession.send(presence);
return presence.getId();
}
/**
* Cancels a previously granted subscription or denies a subscription request.
*
* This basically means that the contact won't receive presence information from you.
*
*
* @param jid The contact's JID, whose subscription is denied or canceled.
* @return The id, which is used for the subscription denial.
* @see 3.2.1. Client Generation of Subscription Cancellation
*/
public final String denySubscription(Jid jid) {
// For tracking purposes, a client SHOULD include an 'id' attribute in a subscription approval or subscription denial; this 'id' attribute MUST NOT mirror the 'id' attribute of the subscription request.
Presence presence = new Presence(jid, Presence.Type.UNSUBSCRIBED, null, UUID.randomUUID().toString());
xmppSession.send(presence);
return presence.getId();
}
/**
* Unsubscribes from a contact's presence.
*
* That basically means, that you will not receive any presence information from the contact anymore.
*
*
* @param jid The contact's JID.
* @return The id, which is used for the unsubscription.
* @see 3.3.1. Client Generation of Unsubscribe
*/
public final String unsubscribe(Jid jid) {
// For tracking purposes, a client SHOULD include an 'id' attribute in a subscription approval or subscription denial; this 'id' attribute MUST NOT mirror the 'id' attribute of the subscription request.
Presence presence = new Presence(jid, Presence.Type.UNSUBSCRIBE, null, UUID.randomUUID().toString());
xmppSession.send(presence);
return presence.getId();
}
/**
* Gets the last sent (non-directed) presence, that has been broadcast by the server.
*
* @return The presence.
*/
public final Presence getLastSentPresence() {
return lastSentPresences.get("");
}
/**
* Gets the last sent presences, that have been sent, including directed presences.
*
* @return The presence.
*/
public final Collection getLastSentPresences() {
return lastSentPresences.values();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy