
org.jivesoftware.whack.ExternalComponentManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Whack is a Java library that easily allows the creation of external components that follow the XEP-0114: Jabber Component Protocol.
The newest version!
/**
* Copyright 2005 Jive Software, 2024 Ignite Realtime Foundation.
*
* 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.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.component.IQResultListener;
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 {
private static final Logger Logger = LoggerFactory.getLogger(ExternalComponentManager.class);
/**
* 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;
/**
* Defines if sockets are started in encrypted mode ("old-style" TLS/SSL, non-STARTTLS
*/
private boolean startEncrypted;
/**
* 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();
Preferences preferences = Preferences.userRoot();
private String preferencesPrefix;
/**
* 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();
/**
* Constructs a new ExternalComponentManager that will make connections
* to the specified XMPP server on the default external component port (5275).
*
* @param host the IP address or name of the XMPP server to connect to (e.g. "example.com").
*/
public ExternalComponentManager(String host) {
this(host, 5275);
}
/**
* 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.
*/
@Deprecated
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);
}
public ExternalComponentManager(String host, int port, boolean startEncrypted) {
if (host == null) {
throw new IllegalArgumentException("Host of XMPP server cannot be null");
}
this.host = host;
this.port = port;
this.startEncrypted = startEncrypted;
// 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, startEncrypted);
// 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);
}
public String getProperty(String name) {
return preferences.get(getPreferencesPrefix() + name, null);
}
public void setProperty(String name, String value) {
preferences.put(getPreferencesPrefix() + name, value);
}
private String getPreferencesPrefix() {
if (preferencesPrefix == null) {
preferencesPrefix = "whack." + domain + ".";
}
return preferencesPrefix;
}
/**
* 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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy