com.adspore.splat.xep0030.DiscoInfoHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.adspore.splat.xep0030.DiscoInfoHandler.java

Source

/**
 * $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);
    }
}