
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