Java tutorial
/** * $RCSfile: IQDiscoInfoHandler.java,v $ * $Revision: 2859 $ * $Date: 2005-09-22 02:30:39 -0300 (Thu, 22 Sep 2005) $ * * Copyright (C) 2004-2008 Jive Software. All rights reserved. * * 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 com.adspore.splat.xep0030; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.component.AbstractComponent; import org.xmpp.forms.DataForm; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; import org.xmpp.packet.PacketError; import org.xmpp.resultsetmanagement.ResultSet; import com.adspore.contracts.IContext; import com.adspore.splat.SplatComponent; import com.adspore.util.JIDUtils; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; /** * Originally supplied a part of Openfire, refactored to work with AbstractComponent plug-ins to * Openfire. * * * */ public class DiscoInfoHandler { private static final Logger mLogger = LoggerFactory.getLogger(DiscoInfoHandler.class); protected final IContext mContext; protected final SplatComponent mComponent; /** * Contains list of nodes that DiscoInfoHandler can report on. If a query for an item * that is not in the list is received, the standard 'no-info' message will be returned. */ protected final Multimap<String, DiscoInfoProvider> mEntities; private List<Element> anonymousUserIdentities = new ArrayList<Element>(); private List<Element> registeredUserIdentities = new ArrayList<Element>(); public DiscoInfoHandler(IContext context, SplatComponent component) { mContext = context; mComponent = component; mEntities = HashMultimap.create(); } public void initialize() { // Initialize the user identity and features collections (optimization to avoid creating // the same objects for each response) addAnonymousUser(); addRegisteredUser(); } protected void addAnonymousUser() { Element userIdentity = DocumentHelper.createElement("identity"); userIdentity.addAttribute("category", "account"); userIdentity.addAttribute("type", "anonymous"); anonymousUserIdentities.add(userIdentity); } protected void addRegisteredUser() { Element userIdentity = DocumentHelper.createElement("identity"); userIdentity.addAttribute("category", "account"); userIdentity.addAttribute("type", "registered"); registeredUserIdentities.add(userIdentity); } public IQ handleDiscoInfoIQ(IQ packet) { /* * Create a copy of the sent pack that will be used as the reply we need to add * the requested info (query) to the reply as well as the 'error' element as described * in the XEP-30 Service Discovery docs, section 3.1 / example 1,2. * * We'll stuff the 'query' element into the reply, then add identity and feature elements * to that if they exist. */ Element queryElement = packet.getChildElement().createCopy(); IQ reply = IQ.createResultIQ(packet); reply.setChildElement(queryElement); /* * Look for a DiscoInfoProvider associated with the requested entity. * We consider the host of the recipient JID of the packet as the entity. It's the * DiscoInfoProvider responsibility to provide information about the JID's name together * with any possible requested node. * * As part of the refactoring, I moved this to a MultiMap implementation because there * may be multiple DiscoInfoProviders that can speak to a particular node, as a node * may host both PubSub entries as well as other services. * * One of the things that makes this so problematic, is the confusion between the JID's * concept of 'Node' and the Entity's concept of 'Node'. When referring to PubSub, the * node is really placed in a JID's 'resource', and the JID uses the 'Node' to identify * the User's name. See example: * * nodeId@domainFoo.domainBar/resource * * When using the JID+Node approach, this goes very wrong.... * */ final Element iq = packet.getChildElement(); final String node = queryElement.attributeValue("node"); final JID targetJID = (null != node) ? JIDUtils.setNodeIDAsResource(packet.getTo(), node) : packet.getTo(); final JID senderJID = packet.getFrom(); /* * Flags controlling the addition of 'standard' features/identities applicable to the * entire result set. Added if ANY of the infoProviders say they do. */ boolean hasDiscoInfoFeature = false; boolean hasDiscoItemsFeature = false; boolean hasResultSetManagementFeature = false; /* * Lookup all possible DiscoInfoProviders that service the target. Base this on the * 'domain' field of the JID, patch up with username later if required. */ Collection<DiscoInfoProvider> infoProviders = getProviders(targetJID.toBareJID()); if (!infoProviders.isEmpty()) { /* * Begin looping through the providers, checking with each to provide them with the * opportunity to add information if they have any to add. */ Iterator<DiscoInfoProvider> itor = infoProviders.iterator(); while (itor.hasNext()) { DiscoInfoProvider possibleProvider = itor.next(); if (possibleProvider.hasInfo(targetJID, senderJID)) { // Add the info that this provider says it has... Iterator<Element> identities = possibleProvider.getIdentities(targetJID, senderJID); while (identities.hasNext()) { queryElement.add(identities.next().createCopy()); } // Add to the reply all the features provided by the DiscoInfoProvider Iterator<String> features = possibleProvider.getFeatures(targetJID, senderJID); while (features.hasNext()) { // Loop through all of the features that this provider has.... final String feature = features.next(); queryElement.addElement("feature").addAttribute("var", feature); if (feature.equals(AbstractComponent.NAMESPACE_DISCO_INFO)) { hasDiscoInfoFeature = true; } else if (feature.equals(AbstractComponent.NAMESPACE_DISCO_ITEMS)) { hasDiscoItemsFeature = true; } else if (feature.equals(ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT)) { hasResultSetManagementFeature = true; } } } if (hasDiscoItemsFeature && !hasResultSetManagementFeature) { // IQDiscoItemsHandler provides result set management // support. queryElement.addElement("feature").addAttribute("var", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT); } // Add to the reply the extended info (XDataForm) provided by the DiscoInfoProvider DataForm dataForm = possibleProvider.getExtendedInfo(targetJID, packet.getFrom()); if (dataForm != null) { queryElement.add(dataForm.getElement()); } } } else { // If we didn't find a DiscoInfoProvider then answer a not found error reply.setError(PacketError.Condition.item_not_found); } return reply; } /** * Returns the DiscoInfoProvider responsible for providing information about a given entity or * null if none was found. * * @param name the name of the identity. * @return the DiscoInfoProvider responsible for providing information about a given entity or * null if none was found. */ public Collection<DiscoInfoProvider> getProviders(String name) { return mEntities.get(name); } /** * Sets that a given DiscoInfoProvider will provide information about a given entity. This * message must be used when new modules (e.g. MUC) are implemented and need to provide * information about them. * * @param name the name of the entity. * @param provider the DiscoInfoProvider that will provide the entity's information. */ public boolean setProvider(String name, DiscoInfoProvider provider) { mLogger.debug("#setProvider() Name: {} Provider:{}", name, provider.toString()); return mEntities.put(name, provider); } /** * Removes the DiscoInfoProvider related to a given entity. * * @param name the name of the entity. */ public boolean removeProvider(String name, DiscoInfoProvider provider) { return mEntities.remove(name, provider); } }