You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@labs.apache.org by be...@apache.org on 2008/10/23 07:42:20 UTC
svn commit: r707278 - in /labs/vysper/src/main/java/org/apache/vysper/xmpp:
modules/core/im/handler/ modules/roster/ modules/roster/handler/
resourcebinding/
Author: berndf
Date: Wed Oct 22 22:42:20 2008
New Revision: 707278
URL: http://svn.apache.org/viewvc?rev=707278&view=rev
Log:
[vysper] roster set, presence cancel + unsubscribe
Modified:
labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/core/im/handler/PresenceSubscriptionHandler.java
labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterItem.java
labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterSubscriptionMutator.java
labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/handler/RosterIQHandler.java
labs/vysper/src/main/java/org/apache/vysper/xmpp/resourcebinding/ResourceState.java
Modified: labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/core/im/handler/PresenceSubscriptionHandler.java
URL: http://svn.apache.org/viewvc/labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/core/im/handler/PresenceSubscriptionHandler.java?rev=707278&r1=707277&r2=707278&view=diff
==============================================================================
--- labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/core/im/handler/PresenceSubscriptionHandler.java (original)
+++ labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/core/im/handler/PresenceSubscriptionHandler.java Wed Oct 22 22:42:20 2008
@@ -171,24 +171,41 @@
*/
@SpecCompliant(spec = "RFC3921bis-04", section = "3.3.3")
protected void handleInboundUnsubscription(PresenceStanza stanza, SessionContext sessionContext, ResourceRegistry registry, RosterManager rosterManager) {
- Entity contact = stanza.getFrom();
- Entity user = stanza.getTo();
+ Entity contact = stanza.getFrom();
+ Entity user = stanza.getTo();
- // TODO: remove subscription from user to contact
+ Entity userBareJid = user.getBareJID();
+ Entity contactBareJid = contact.getBareJID();
- // write inbound stanza to the user
+ RosterItem rosterItem;
+ try {
+ rosterItem = rosterManager.getContact(userBareJid, contactBareJid);
+ } catch (RosterException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ if (rosterItem == null) return;
+
+ RosterSubscriptionMutator.Result result = RosterSubscriptionMutator.getInstance().remove(rosterItem, FROM);
+
+ if (result != OK) {
+ // TODO
+ return;
+ }
+
+ // write inbound stanza to the user
sessionContext.getResponseWriter().write(stanza);
- List<String> resources = registry.getInterestedResources(user);
- for (String resource : resources) {
- Entity userResource = new EntityImpl(user, resource);
- // TODO: determine the right subscription: 'none' or 'to'
- Stanza push = buildRosterPushStanza(userResource,
- sessionContext.nextSequenceValue(), contact.getBareJID(),
- null /* TODO subType*/, null /*TODO ask? */);
+ // send roster push to all interested resources
+ // TODO do this only once, since inbound is multiplexed on DeliveringInboundStanzaRelay level already
+ List<String> resources = registry.getInterestedResources(user);
+ for (String resource : resources) {
+ Entity userResource = new EntityImpl(user, resource);
+ Stanza push = buildRosterPushStanza(userResource, sessionContext.nextSequenceValue(), rosterItem);
+ sessionContext.getResponseWriter().write(push);
+ }
- relayStanza(userResource, push, sessionContext);
- }
}
/*
@@ -238,79 +255,118 @@
@SpecCompliant(spec = "RFC3921bis-04", section = "3.3.2")
protected void handleOutboundUnsubscription(PresenceStanza stanza,
SessionContext sessionContext, ResourceRegistry registry, RosterManager rosterManager) {
- Entity user = stanza.getFrom();
- Entity contact = stanza.getTo();
+ Entity user = stanza.getFrom();
+ Entity contact = stanza.getTo();
- relayStanza(contact, stanza, sessionContext);
+ Entity userBareJid = user.getBareJID();
+ Entity contactBareJid = contact.getBareJID();
- List<String> resources = registry.getInterestedResources(user);
- for (String resource : resources) {
- Entity userResource = new EntityImpl(user, resource);
- // TODO: determine the correct subscription state: 'none' or 'from'
- Stanza push = buildRosterPushStanza(userResource,
- sessionContext.nextSequenceValue(), contact.getBareJID(),
- null /* TODO subType*/, null /*TODO ask? */);
- relayStanza(userResource, push, sessionContext);
- }
+ relayStanza(contact, stanza, sessionContext);
- }
+ RosterItem rosterItem = null;
+ try {
+ rosterItem = rosterManager.getContact(userBareJid, contactBareJid);
+ } catch (RosterException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
-/*
-FROM: http://www.xmpp.org/internet-drafts/draft-saintandre-rfc3921bis-05.txt
+ if (rosterItem == null) return;
-3.2.3. Server Processing of Inbound Subscription Cancellation
+ RosterSubscriptionMutator.Result result = RosterSubscriptionMutator.getInstance().remove(rosterItem, TO);
- When the user's server receives the inbound subscription
- cancellation, it MUST modify the subscription state and send a roster
- push to the user's interested resources, where the subscription state
- is now either "none" or "from" (see Appendix A).
+ if (result != OK) {
+ // TODO
+ return;
+ }
- US: <iq to='romeo@example.net/foo'
- type='set'
- id='h37h3u1bv400'>
- <query xmlns='jabber:iq:roster'>
- <item jid='juliet@example.com'
- subscription='none'/>
- </query>
- </iq>
+ relayStanza(contact, stanza, sessionContext);
- US: <iq to='romeo@example.net/bar'
- type='set'
- id='h37h3u1bv401'>
- <query xmlns='jabber:iq:roster'>
- <item jid='juliet@example.com'
- subscription='none'/>
- </item>
- </query>
- </iq>
+ sendRosterUpdate(sessionContext, registry, user, rosterItem);
+ }
+ /**
+ * send roster push to all of the user's interested resources
+ */
+ protected void sendRosterUpdate(SessionContext sessionContext, ResourceRegistry registry, Entity user, RosterItem rosterItem) {
+ List<String> resources = registry.getInterestedResources(user);
- */
+ for (String resource : resources) {
+ Entity userResource = new EntityImpl(user, resource);
+ Stanza push = buildRosterPushStanza(userResource, sessionContext.nextSequenceValue(), rosterItem);
+ relayStanza(userResource, push, sessionContext);
+ }
+ }
+
+ /*
+ FROM: http://www.xmpp.org/internet-drafts/draft-saintandre-rfc3921bis-05.txt
+
+ 3.2.3. Server Processing of Inbound Subscription Cancellation
+
+ When the user's server receives the inbound subscription
+ cancellation, it MUST modify the subscription state and send a roster
+ push to the user's interested resources, where the subscription state
+ is now either "none" or "from" (see Appendix A).
+
+ US: <iq to='romeo@example.net/foo'
+ type='set'
+ id='h37h3u1bv400'>
+ <query xmlns='jabber:iq:roster'>
+ <item jid='juliet@example.com'
+ subscription='none'/>
+ </query>
+ </iq>
+
+ US: <iq to='romeo@example.net/bar'
+ type='set'
+ id='h37h3u1bv401'>
+ <query xmlns='jabber:iq:roster'>
+ <item jid='juliet@example.com'
+ subscription='none'/>
+ </item>
+ </query>
+ </iq>
+
+
+ */
@SpecCompliant(spec = "RFC3921bis-04", section = "3.2.3")
protected void handleInboundSubscriptionCancellation(PresenceStanza stanza,
SessionContext sessionContext, ResourceRegistry registry, RosterManager rosterManager) {
- // TODO: check if client actually requested subscription
- // TODO: update roster for user
- // TODO: if current subscription is either 'both' or
+ Entity contact = stanza.getFrom();
+ Entity user = stanza.getTo();
- // write inbound stanza to the user
+ Entity userBareJid = user.getBareJID();
+ Entity contactBareJid = contact.getBareJID();
+
+ RosterItem rosterItem;
+ try {
+ rosterItem = rosterManager.getContact(userBareJid, contactBareJid);
+ } catch (RosterException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ if (rosterItem == null) return;
+
+ RosterSubscriptionMutator.Result result = RosterSubscriptionMutator.getInstance().remove(rosterItem, TO);
+
+ if (result != OK) {
+ // TODO
+ return;
+ }
+
+ // write inbound stanza to the user
sessionContext.getResponseWriter().write(stanza);
-
-
- Entity contact = stanza.getFrom();
- Entity user = stanza.getTo();
- // TODO do this only once, since inbound is multiplexed on DeliveringInboundStanzaRelay level already
- List<String> resources = registry.getInterestedResources(user);
- for (String resource : resources) {
- Entity userResource = new EntityImpl(user, resource);
- // TODO: determine the correct subscription state
- Stanza push = buildRosterPushStanza(userResource,
- sessionContext.nextSequenceValue(), contact.getBareJID(),
- null /* TODO subType*/, null /*TODO ask? */);
+ // send roster push to all interested resources
+ // TODO do this only once, since inbound is multiplexed on DeliveringInboundStanzaRelay level already
+ List<String> resources = registry.getInterestedResources(user);
+ for (String resource : resources) {
+ Entity userResource = new EntityImpl(user, resource);
+ Stanza push = buildRosterPushStanza(userResource, sessionContext.nextSequenceValue(), rosterItem);
sessionContext.getResponseWriter().write(push);
- }
+ }
}
/*
@@ -362,19 +418,30 @@
Entity user = stanza.getFrom();
Entity contact = stanza.getTo();
- relayStanza(contact, stanza, sessionContext);
+ Entity userBareJid = user.getBareJID();
+ Entity contactBareJid = contact.getBareJID();
+
+ RosterItem rosterItem = null;
+ try {
+ rosterItem = rosterManager.getContact(userBareJid, contactBareJid);
+ } catch (RosterException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
- // send roster push to all of the user's interested resources
- List<String> resources = registry.getInterestedResources(user);
- for (String resource : resources) {
- Entity userResource = new EntityImpl(user, resource);
- // TODO: determine the right subscription: 'to' or 'none'
- Stanza push = buildRosterPushStanza(userResource,
- sessionContext.nextSequenceValue(), contact.getBareJID(),
- null /* TODO subType*/, null /*TODO ask? */);
+ if (rosterItem == null) return;
+
+ RosterSubscriptionMutator.Result result = RosterSubscriptionMutator.getInstance().remove(rosterItem, FROM);
- relayStanza(userResource, push, sessionContext);
- }
+ if (result != OK) {
+ // TODO
+ return;
+ }
+
+ relayStanza(contact, stanza, sessionContext);
+
+ // send roster push to all of the user's interested resources
+ sendRosterUpdate(sessionContext, registry, user, rosterItem);
}
@@ -461,17 +528,11 @@
relayStanza(contact, stanza, sessionContext);
// send roster push to all of the user's interested resources
- List<String> resources = registry.getInterestedResources(user);
-
- for (String resource : resources) {
- Entity userResource = new EntityImpl(user, resource);
- Stanza push = buildRosterPushStanza(userResource, sessionContext.nextSequenceValue(), rosterItem);
- relayStanza(userResource, push, sessionContext);
- }
+ sendRosterUpdate(sessionContext, registry, user, rosterItem);
// send presence information from user's available resource to the
// contact
- resources = registry.getAvailableResources(user);
+ List<String> resources = registry.getAvailableResources(user);
for (String resource : resources) {
Entity userResource = new EntityImpl(user, resource);
// TODO check: send real presence, or initial pres?
Modified: labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterItem.java
URL: http://svn.apache.org/viewvc/labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterItem.java?rev=707278&r1=707277&r2=707278&view=diff
==============================================================================
--- labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterItem.java (original)
+++ labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterItem.java Wed Oct 22 22:42:20 2008
@@ -50,6 +50,11 @@
this.askSubscriptionType = askSubscriptionType == null ? NOT_SET : askSubscriptionType;
}
+ public RosterItem(Entity jid, String name, SubscriptionType subscriptionType, AskSubscriptionType askSubscriptionType, List<RosterGroup> groups) {
+ this(jid, name, subscriptionType, askSubscriptionType);
+ this.groups.addAll(groups);
+ }
+
public Entity getJid() {
return jid;
}
Modified: labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterSubscriptionMutator.java
URL: http://svn.apache.org/viewvc/labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterSubscriptionMutator.java?rev=707278&r1=707277&r2=707278&view=diff
==============================================================================
--- labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterSubscriptionMutator.java (original)
+++ labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/RosterSubscriptionMutator.java Wed Oct 22 22:42:20 2008
@@ -150,9 +150,9 @@
case REMOVE:
return Result.ILLEGAL_ARGUMENT;
case FROM:
- return removeTo(item);
- case TO:
return removeFrom(item);
+ case TO:
+ return removeTo(item);
default:
throw new IllegalArgumentException("unhandled SubscriptionType " + removeSubscriptionType.value());
}
@@ -160,7 +160,13 @@
protected Result removeTo(RosterItem item) {
SubscriptionType type = item.getSubscriptionType();
- if (!type.includesTo()) return ALREADY_SET;
+ if (!type.includesTo()) {
+ // if sub was asked, remove that.
+ AskSubscriptionType askType = item.getAskSubscriptionType();
+ if (askType != ASK_SUBSCRIBE) return ALREADY_SET;
+ item.setAskSubscriptionType(NOT_SET);
+ return OK;
+ }
if (type == BOTH) {
type = FROM;
} else if (type == TO) {
@@ -172,7 +178,13 @@
protected Result removeFrom(RosterItem item) {
SubscriptionType type = item.getSubscriptionType();
- if (!type.includesFrom()) return ALREADY_SET;
+ if (!type.includesFrom()) {
+ // if sub was asked, remove that.
+ AskSubscriptionType askType = item.getAskSubscriptionType();
+ if (askType != ASK_SUBSCRIBED) return ALREADY_SET;
+ item.setAskSubscriptionType(NOT_SET);
+ return OK;
+ }
if (type == BOTH) {
type = TO;
} else if (type == FROM) {
Modified: labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/handler/RosterIQHandler.java
URL: http://svn.apache.org/viewvc/labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/handler/RosterIQHandler.java?rev=707278&r1=707277&r2=707278&view=diff
==============================================================================
--- labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/handler/RosterIQHandler.java (original)
+++ labs/vysper/src/main/java/org/apache/vysper/xmpp/modules/roster/handler/RosterIQHandler.java Wed Oct 22 22:42:20 2008
@@ -18,9 +18,7 @@
package org.apache.vysper.xmpp.modules.roster.handler;
import org.apache.vysper.xmpp.modules.core.base.handler.IQHandler;
-import org.apache.vysper.xmpp.modules.roster.Roster;
-import org.apache.vysper.xmpp.modules.roster.RosterException;
-import org.apache.vysper.xmpp.modules.roster.RosterStanzaUtils;
+import org.apache.vysper.xmpp.modules.roster.*;
import org.apache.vysper.xmpp.modules.roster.persistence.RosterManager;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
import org.apache.vysper.xmpp.resourcebinding.ResourceRegistry;
@@ -32,6 +30,15 @@
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.stanza.StanzaBuilder;
import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.addressing.EntityImpl;
+import org.apache.vysper.xmpp.addressing.EntityFormatException;
+import org.apache.vysper.xmpp.xmlfragment.XMLElement;
+import org.apache.vysper.xmpp.xmlfragment.XMLSemanticError;
+import org.apache.vysper.xmpp.xmlfragment.Attribute;
+import org.apache.vysper.xmpp.xmlfragment.XMLElementVerifier;
+
+import java.util.List;
+import java.util.ArrayList;
/**
* handles roster get, set, push & result requests
@@ -58,8 +65,7 @@
new RuntimeException("iq stanza type RESULT not yet handled");
break;
case SET:
- new RuntimeException("iq stanza type SET not yet handled");
- break;
+ return handleRosterSet(stanza, sessionContext);
default:
new RuntimeException("iq stanza type not supported: " + stanza.getIQType());
}
@@ -75,24 +81,16 @@
if (rosterManager == null) {
return handleCannotRetrieveRoster(stanza, sessionContext);
- }
-
+ }
+
String resourceId = registry.getFirstResourceForSession(sessionContext);
ResourceState currentState = registry.getResourceState(resourceId);
if (currentState != null) {
- if (currentState == ResourceState.CONNECTED) {
- registry.setResourceState(resourceId, ResourceState.INTERESTED_NOT_YET_AVAILABLE);
- } else if (currentState == ResourceState.AVAILABLE) {
- registry.setResourceState(resourceId, ResourceState.INTERESTED);
- }
+ registry.setResourceState(resourceId, ResourceState.makeInterested(currentState));
}
- Entity from = stanza.getFrom();
- if (from == null) {
- if (sessionContext.getInitiatingEntity() == null) throw new RuntimeException("handle this case");
- else from = sessionContext.getInitiatingEntity();
- }
-
+ Entity from = determineFrom(stanza, sessionContext);
+
Roster roster = null;
try {
roster = rosterManager.retrieve(from.getBareJID());
@@ -106,6 +104,125 @@
return stanzaBuilder.getFinalStanza();
}
+ private Entity determineFrom(IQStanza stanza, SessionContext sessionContext) {
+ Entity from = stanza.getFrom();
+ if (from == null) {
+ if (sessionContext.getInitiatingEntity() == null) throw new RuntimeException("handle this case");
+ else from = sessionContext.getInitiatingEntity();
+ }
+ return from;
+ }
+
+ /*
+ 2.1.3. Roster Set
+
+
+ A ROSTER SET is a client's request for the server to modify (i.e.,
+ create, update, or delete) a roster item; syntactically it is an IQ
+ stanza of type "set" sent from client to server and containing a
+ <query/> element qualified by the 'jabber:iq:roster' namespace.
+
+ The following rules apply to roster sets:
+
+ 1. The <query/> element MUST contain one and only one <item/>
+ element.
+ 2. The server MUST ignore any value of the 'subscription' attribute
+ other than "remove" (see Section 2.1.6).
+ 3. The server MUST ignore any 'to' address specified on the IQ
+ stanza and MUST handle the IQ stanza as if it included no 'to'
+ attribute.
+
+ C: <iq from='juliet@example.com/balcony'
+ id='rs1'
+ type='set'>
+ <query xmlns='jabber:iq:roster'>
+ <item jid='nurse@example.com'/>
+ </query>
+ </iq>
+
+ */
+ protected Stanza handleRosterSet(IQStanza stanza, SessionContext sessionContext) {
+ ServerRuntimeContext serverContext = sessionContext.getServerRuntimeContext();
+ ResourceRegistry registry = serverContext.getResourceRegistry();
+ RosterManager rosterManager = (RosterManager)serverContext.getServerRuntimeContextService(RosterManager.SERVER_SERVICE_ROSTERMANAGER);
+
+ if (rosterManager == null) {
+ return handleCannotRetrieveRoster(stanza, sessionContext);
+ }
+
+ Entity user = determineFrom(stanza, sessionContext);
+
+ XMLElement itemElement = null;
+ RosterItem setRosterItem = null;
+ try {
+ XMLElement queryElement = stanza.getSingleInnerElementsNamed("query");
+ if (queryElement != null) {
+ itemElement = queryElement.getSingleInnerElementsNamed("item");
+
+ setRosterItem = parseRosterItem(itemElement);
+ }
+ } catch (XMLSemanticError xmlSemanticError) {
+ xmlSemanticError.printStackTrace();
+ throw new RuntimeException("roster set needs proper item node", xmlSemanticError);
+ }
+
+ RosterItem existingItem;
+ try {
+ existingItem = rosterManager.getContact(user.getBareJID(), setRosterItem.getJid().getBareJID());
+ } catch (RosterException e) {
+ existingItem = null;
+ }
+
+ if (setRosterItem.getSubscriptionType() == SubscriptionType.REMOVE) {
+ throw new RuntimeException("roster set REMOVE not yet implemented");
+ }
+
+ if (existingItem == null) {
+ // TODO
+ return null;
+ }
+
+ if (setRosterItem.getName() != null) {
+ System.out.println("set name to " + setRosterItem.getName());
+ }
+
+ return null;
+ }
+
+ private RosterItem parseRosterItem(XMLElement itemElement) throws org.apache.vysper.xmpp.xmlfragment.XMLSemanticError {
+
+ if (itemElement == null) {
+ throw new XMLSemanticError("missing 'item' element");
+ }
+ Attribute attributeJID = itemElement.getAttribute("jid");
+ if (attributeJID == null || attributeJID.getValue() == null) throw new XMLSemanticError("missing 'jid' attribute");
+
+ XMLElementVerifier verifier = itemElement.getVerifier();
+ String name = verifier.attributePresent("name") ? itemElement.getAttribute("name").getValue() : null;
+ SubscriptionType subscription = verifier.attributePresent("subscription") ? SubscriptionType.valueOf(itemElement.getAttribute("subscription").getValue()) : null;
+
+ String contactJid = attributeJID.getValue();
+ Entity contact;
+ try {
+ contact = EntityImpl.parse(contactJid);
+ } catch (EntityFormatException e) {
+ throw new XMLSemanticError("jid cannot be parsed: " + contactJid);
+ }
+
+ List<RosterGroup> groups = new ArrayList<RosterGroup>();
+ List<XMLElement> groupElements = itemElement.getInnerElementsNamed("group");
+ if (groupElements != null) {
+ for (XMLElement groupElement : groupElements) {
+ String groupName = groupElement.getSingleInnerText().getText();
+ if (groupName == null || groupName.length() == 0) continue;
+ groups.add(new RosterGroup(groupName));
+ }
+ }
+
+ RosterItem rosterItem = new RosterItem(contact, name, subscription, AskSubscriptionType.NOT_SET, groups);
+ return rosterItem;
+ }
+
private Stanza handleCannotRetrieveRoster(IQStanza stanza, SessionContext sessionContext) {
throw new RuntimeException("gracefully handling roster management problem not implemented");
}
Modified: labs/vysper/src/main/java/org/apache/vysper/xmpp/resourcebinding/ResourceState.java
URL: http://svn.apache.org/viewvc/labs/vysper/src/main/java/org/apache/vysper/xmpp/resourcebinding/ResourceState.java?rev=707278&r1=707277&r2=707278&view=diff
==============================================================================
--- labs/vysper/src/main/java/org/apache/vysper/xmpp/resourcebinding/ResourceState.java (original)
+++ labs/vysper/src/main/java/org/apache/vysper/xmpp/resourcebinding/ResourceState.java Wed Oct 22 22:42:20 2008
@@ -50,4 +50,9 @@
if (inState == null || !isInterested(inState)) return AVAILABLE;
return INTERESTED;
}
+
+ public static ResourceState makeInterested(ResourceState inState) {
+ if (inState == AVAILABLE) return INTERESTED;
+ return INTERESTED_NOT_YET_AVAILABLE;
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@labs.apache.org
For additional commands, e-mail: commits-help@labs.apache.org