Java程序  |  214行  |  6.64 KB

/**
 * Copyright 2013 Georg Lukas
 *
 * 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 org.jivesoftware.smackx.carbons;

import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;

import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.packet.DiscoverInfo;

/**
 * Packet extension for XEP-0280: Message Carbons. This class implements
 * the manager for registering {@link Carbon} support, enabling and disabling
 * message carbons.
 *
 * You should call enableCarbons() before sending your first undirected
 * presence.
 *
 * @author Georg Lukas
 */
public class CarbonManager {

    private static Map<Connection, CarbonManager> instances =
            Collections.synchronizedMap(new WeakHashMap<Connection, CarbonManager>());

    static {
        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
            public void connectionCreated(Connection connection) {
                new CarbonManager(connection);
            }
        });
    }
    
    private Connection connection;
    private volatile boolean enabled_state = false;

    private CarbonManager(Connection connection) {
        ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
        sdm.addFeature(Carbon.NAMESPACE);
        this.connection = connection;
        instances.put(connection, this);
    }

    /**
     * Obtain the CarbonManager responsible for a connection.
     *
     * @param connection the connection object.
     *
     * @return a CarbonManager instance
     */
    public static CarbonManager getInstanceFor(Connection connection) {
        CarbonManager carbonManager = instances.get(connection);

        if (carbonManager == null) {
            carbonManager = new CarbonManager(connection);
        }

        return carbonManager;
    }

    private IQ carbonsEnabledIQ(final boolean new_state) {
        IQ setIQ = new IQ() {
            public String getChildElementXML() {
                return "<" + (new_state? "enable" : "disable") + " xmlns='" + Carbon.NAMESPACE + "'/>";
            }
        };
        setIQ.setType(IQ.Type.SET);
        return setIQ;
    }

    /**
     * Returns true if XMPP Carbons are supported by the server.
     * 
     * @return true if supported
     */
    public boolean isSupportedByServer() {
        try {
            DiscoverInfo result = ServiceDiscoveryManager
                .getInstanceFor(connection).discoverInfo(connection.getServiceName());
            return result.containsFeature(Carbon.NAMESPACE);
        }
        catch (XMPPException e) {
            return false;
        }
    }

    /**
     * Notify server to change the carbons state. This method returns
     * immediately and changes the variable when the reply arrives.
     *
     * You should first check for support using isSupportedByServer().
     *
     * @param new_state whether carbons should be enabled or disabled
     */
    public void sendCarbonsEnabled(final boolean new_state) {
        IQ setIQ = carbonsEnabledIQ(new_state);

        connection.addPacketListener(new PacketListener() {
            public void processPacket(Packet packet) {
                IQ result = (IQ)packet;
                if (result.getType() == IQ.Type.RESULT) {
                    enabled_state = new_state;
                }
                connection.removePacketListener(this);
            }
        }, new PacketIDFilter(setIQ.getPacketID()));

        connection.sendPacket(setIQ);
    }

    /**
     * Notify server to change the carbons state. This method blocks
     * some time until the server replies to the IQ and returns true on
     * success.
     *
     * You should first check for support using isSupportedByServer().
     *
     * @param new_state whether carbons should be enabled or disabled
     *
     * @return true if the operation was successful
     */
    public boolean setCarbonsEnabled(final boolean new_state) {
        if (enabled_state == new_state)
            return true;

        IQ setIQ = carbonsEnabledIQ(new_state);

        PacketCollector collector =
                connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID()));
        connection.sendPacket(setIQ);
        IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
        collector.cancel();

        if (result != null && result.getType() == IQ.Type.RESULT) {
            enabled_state = new_state;
            return true;
        }
        return false;
    }

    /**
     * Helper method to enable carbons.
     *
     * @return true if the operation was successful
     */
    public boolean enableCarbons() {
        return setCarbonsEnabled(true);
    }

    /**
     * Helper method to disable carbons.
     *
     * @return true if the operation was successful
     */
    public boolean disableCarbons() {
        return setCarbonsEnabled(false);
    }

    /**
     * Check if carbons are enabled on this connection.
     */
    public boolean getCarbonsEnabled() {
        return this.enabled_state;
    }

    /**
     * Obtain a Carbon from a message, if available.
     *
     * @param msg Message object to check for carbons
     *
     * @return a Carbon if available, null otherwise.
     */
    public static Carbon getCarbon(Message msg) {
        Carbon cc = (Carbon)msg.getExtension("received", Carbon.NAMESPACE);
        if (cc == null)
            cc = (Carbon)msg.getExtension("sent", Carbon.NAMESPACE);
        return cc;
    }

    /**
     * Mark a message as "private", so it will not be carbon-copied.
     *
     * @param msg Message object to mark private
     */
    public static void disableCarbons(Message msg) {
        msg.addExtension(new Carbon.Private());
    }
}