All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.whack.ExternalComponentManager Maven / Gradle / Ivy

/**
 * $RCSfile$
 * $Revision: 10099 $
 * $Date: 2008-03-20 16:46:17 -0700 (Thu, 20 Mar 2008) $
 *
 * Copyright 2005 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 org.jivesoftware.whack;

import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;

/**
 * Implementation of the ComponentManager interface for external components.
 * This implementation follows JEP-0014.
 * 
 * @author Matt Tucker
 */
public class ExternalComponentManager implements ComponentManager {

    /**
     * Keeps the IP address or hostname of the server. This value will be used
     * only for creating connections.
     */
    private String host;
    /**
     * Port of the server used for establishing new connections.
     */
    private int port;
    /**
     * Keeps the domain of the XMPP server. The domain may or may not match the
     * host. The domain will be used mainly for the XMPP packets while the host
     * is used mainly for creating connections to the server.
     */
    private String domain;
    /**
     * Timeout to use when trying to connect to the server.
     */
    private int connectTimeout = 2000;
    /**
     * This is a global secret key that will be used during the handshake with
     * the server. If a secret key was not defined for the specific component
     * then the global secret key will be used.
     */
    private String defaultSecretKey;
    /**
     * Keeps the secret keys to use for each subdomain. If a key was not found
     * for a specific subdomain then the global secret key will used for the
     * handshake with the server.
     */
    private Map secretKeys = new Hashtable();
    /**
     * Holds the settings for whether we will tell the XMPP server that a given
     * component can connect to the same JID multiple times. This is a custom
     * Openfire extension and will not work with any other XMPP server. Other
     * servers should ignore this setting.
     */
    private Map allowMultiple = new Hashtable();

    /**
     * Keeps a map that associates a domain with the external component thas is
     * handling the domain.
     */
    private Map componentsByDomain = new Hashtable();
    /**
     * Keeps a map that associates a component with the wrapping
     * ExternalComponent.
     */
    private Map components = new Hashtable();

    private static final Logger LOG = Logger.getLogger(ExternalComponent.class);

    private Properties properties = new Properties();

    /**
     * Constructs a new ExternalComponentManager that will make connections to
     * the specified XMPP server on the default port (5222).
     * 
     * @param host
     *            the IP address or name of the XMPP server to connect to (e.g.
     *            "example.com").
     */
    public ExternalComponentManager(String host) {
        this(host, 5225);
    }

    /**
     * Constructs a new ExternalComponentManager that will make connections to
     * the specified XMPP server on the given port.
     * 
     * @param host
     *            the IP address or name of the XMPP server to connect to (e.g.
     *            "example.com").
     * @param port
     *            the port to connect on.
     */
    public ExternalComponentManager(String host, int port) {
        if (host == null) {
            throw new IllegalArgumentException(
                    "Host of XMPP server cannot be null");
        }
        this.host = host;
        this.port = port;

        // Set this ComponentManager as the current component manager
        ComponentManagerFactory.setComponentManager(this);
    }

    /**
     * Sets a secret key for a sub-domain, for future use by a component
     * connecting to the server. Keys are used as an authentication mechanism
     * when connecting to the server. Some servers may require a different key
     * for each component, while others may use a global secret key.
     * 
     * @param subdomain
     *            the sub-domain.
     * @param secretKey
     *            the secret key
     */
    public void setSecretKey(String subdomain, String secretKey) {
        secretKeys.put(subdomain, secretKey);
    }

    /**
     * Returns the secret key for a sub-domain. If no key was found then the
     * default secret key will be returned.
     * 
     * @param subdomain
     *            the subdomain to return its secret key.
     * @return the secret key for a sub-domain.
     */
    public String getSecretKey(String subdomain) {
        // Find the proper secret key to connect as the subdomain.
        String secretKey = secretKeys.get(subdomain);
        if (secretKey == null) {
            secretKey = defaultSecretKey;
        }
        return secretKey;
    }

    /**
     * Sets the default secret key, which will be used when connecting if a
     * specific secret key for the component hasn't been sent. Keys are used as
     * an authentication mechanism when connecting to the server. Some servers
     * may require a different key for each component, while others may use a
     * global secret key.
     * 
     * @param secretKey
     *            the default secret key.
     */
    public void setDefaultSecretKey(String secretKey) {
        this.defaultSecretKey = secretKey;
    }

    /**
     * Returns if we want components to be able to connect multiple times to the
     * same JID. This is a custom Openfire extension and will not work with any
     * other XMPP server. Other XMPP servers should ignore this extra setting.
     * 
     * @param subdomain
     *            the sub-domain.
     * @return True or false if we are allowing multiple connections.
     */
    public boolean isMultipleAllowed(String subdomain) {
        Boolean allowed = allowMultiple.get(subdomain);
        return allowed != null && allowed;
    }

    /**
     * Sets whether we will tell the XMPP server that we want multiple
     * components to be able to connect to the same JID. This is a custom
     * Openfire extension and will not work with any other XMPP server. Other
     * XMPP servers should ignore this extra setting.
     * 
     * @param subdomain
     *            the sub-domain.
     * @param allowMultiple
     *            Set to true if we want to allow multiple connections to same
     *            JID.
     */
    public void setMultipleAllowed(String subdomain, boolean allowMultiple) {
        this.allowMultiple.put(subdomain, allowMultiple);
    }

    public void addComponent(String subdomain, Component component)
            throws ComponentException {
        addComponent(subdomain, component, this.port);
    }

    public void addComponent(String subdomain, Component component, Integer port)
            throws ComponentException {
        if (componentsByDomain.containsKey(subdomain)) {
            if (componentsByDomain.get(subdomain).getComponent() == component) {
                // Do nothing since the component has already been registered
                return;
            } else {
                throw new IllegalArgumentException(
                        "Subdomain already in use by another component");
            }
        }
        // Create a wrapping ExternalComponent on the component
        ExternalComponent externalComponent = new ExternalComponent(component,
                this);
        try {
            // Register the new component
            componentsByDomain.put(subdomain, externalComponent);
            components.put(component, externalComponent);
            // Ask the ExternalComponent to connect with the remote server
            externalComponent.connect(host, port, subdomain);
            // Initialize the component
            JID componentJID = new JID(null, externalComponent.getDomain(),
                    null);
            externalComponent.initialize(componentJID, this);
        } catch (ComponentException e) {
            // Unregister the new component
            componentsByDomain.remove(subdomain);
            components.remove(component);
            // Re-throw the exception
            throw e;
        }
        // Ask the external component to start processing incoming packets
        externalComponent.start();
    }

    public void removeComponent(String subdomain) throws ComponentException {
        ExternalComponent externalComponent = componentsByDomain
                .remove(subdomain);
        if (externalComponent != null) {
            components.remove(externalComponent.getComponent());
            externalComponent.shutdown();
        }
    }

    public void sendPacket(Component component, Packet packet) {
        // Get the ExternalComponent that is wrapping the specified component
        // and ask it to
        // send the packet
        components.get(component).send(packet);
    }

    public IQ query(Component component, IQ packet, long timeout)
            throws ComponentException {
        final LinkedBlockingQueue answer = new LinkedBlockingQueue(8);
        ExternalComponent externalComponent = components.get(component);
        externalComponent.addIQResultListener(packet.getID(),
                new IQResultListener() {
                    public void receivedAnswer(IQ packet) {
                        answer.offer(packet);
                    }

                    public void answerTimeout(String packetId) {
                        // Do nothing
                    }
                }, timeout);
        sendPacket(component, packet);
        IQ reply = null;
        try {
            reply = answer.poll(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            // Ignore
        }
        return reply;
    }

    public void query(Component component, IQ packet, IQResultListener listener)
            throws ComponentException {
        ExternalComponent externalComponent = components.get(component);
        // Add listenet with a timeout of 5 minutes to prevent memory leaks
        externalComponent.addIQResultListener(packet.getID(), listener, 300000);
        sendPacket(component, packet);
    }

    /**
     * Sets the domain of the XMPP server. The domain may or may not match the
     * host. The domain will be used mainly for the XMPP packets while the host
     * is used mainly for creating connections to the server.
     * 
     * @param domain
     *            the domain of the XMPP server.
     */
    public void setServerName(String domain) {
        this.domain = domain;
    }

    /**
     * Returns the domain of the XMPP server where we are connected to or
     * null if this value was never configured. When the value is null
     * then the component will register with just its subdomain and we expect
     * the server to accept the component and append its domain to form the JID
     * of the component.
     * 
     * @return the domain of the XMPP server or null if never configured.
     */
    public String getServerName() {
        return domain;
    }

    /**
     * Returns the timeout (in milliseconds) to use when trying to connect to
     * the server. The default value is 2 seconds.
     * 
     * @return the timeout to use when trying to connect to the server.
     */
    public int getConnectTimeout() {
        return connectTimeout;
    }

    /**
     * Sets the timeout (in milliseconds) to use when trying to connect to the
     * server. The default value is 2 seconds.
     * 
     * @param connectTimeout
     *            the timeout, in milliseconds, to use when trying to connect to
     *            the server.
     */
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public boolean isExternalMode() {
        return true;
    }

    public Logger getLog() {
        return LOG;
    }

    @Override
    public void query(Component component, IQ packet,
            org.xmpp.component.IQResultListener listener)
            throws ComponentException {
        // TODO Auto-generated method stub
    }

    @Override
    public String getProperty(String name) {
        return properties.getProperty(name);
    }

    @Override
    public void setProperty(String name, String value) {
        properties.setProperty(name, value);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy