📄 internalcomponentmanager.java
字号:
/**
* $RCSfile$
* $Revision: 3126 $
* $Date: 2005-11-30 15:20:53 -0300 (Wed, 30 Nov 2005) $
*
* Copyright (C) 2008 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.openfire.component;
import org.dom4j.Element;
import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.IQDiscoItemsHandler;
import org.jivesoftware.openfire.session.ComponentSession;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Manages the registration and delegation of Components. The ComponentManager
* is responsible for managing registration and delegation of {@link Component Components},
* as well as offering a facade around basic server functionallity such as sending and
* receiving of packets.<p>
*
* This component manager will be an internal service whose JID will be component.[domain]. So the
* component manager will be able to send packets to other internal or external components and also
* receive packets from other components or even from trusted clients (e.g. ad-hoc commands).
*
* @author Derek DeMoro
*/
public class InternalComponentManager extends BasicModule implements ComponentManager, RoutableChannelHandler {
final private Map<String, RoutableComponents> routables = new ConcurrentHashMap<String, RoutableComponents>();
private Map<String, IQ> componentInfo = new ConcurrentHashMap<String, IQ>();
private Map<JID, JID> presenceMap = new ConcurrentHashMap<JID, JID>();
/**
* Holds the list of listeners that will be notified of component events.
*/
private List<ComponentEventListener> listeners = new CopyOnWriteArrayList<ComponentEventListener>();
private static InternalComponentManager instance;
/**
* XMPP address of this internal service. The address is of the form: component.[domain]
*/
private JID serviceAddress;
/**
* Holds the domain of the server. We are using an iv since we use this value many times
* in many methods.
*/
private String serverDomain;
private RoutingTable routingTable;
public InternalComponentManager() {
super("Internal Component Manager");
instance = this;
}
public static InternalComponentManager getInstance() {
return instance;
}
public void initialize(XMPPServer server) {
super.initialize(server);
routingTable = server.getRoutingTable();
}
public void start() {
// Set this ComponentManager as the current component manager
ComponentManagerFactory.setComponentManager(instance);
XMPPServer server = XMPPServer.getInstance();
serverDomain = server.getServerInfo().getXMPPDomain();
// Set the address of this internal service. component.[domain]
serviceAddress = new JID(null, "component." + serverDomain, null);
if (!server.isSetupMode()) {
// Add a route to this service
server.getRoutingTable().addComponentRoute(getAddress(), this);
}
}
public void stop() {
super.stop();
if (getAddress() != null) {
// Remove the route to this service
XMPPServer.getInstance().getRoutingTable().removeComponentRoute(getAddress());
}
}
public void addComponent(String subdomain, Component component) throws ComponentException {
synchronized (routables) {
RoutableComponents routable = routables.get(subdomain);
if (routable != null && routable.hasComponent(component)) {
// This component has already registered with this subdomain.
// TODO: Is this all we should do? Should we return an error?
return;
}
Log.debug("InternalComponentManager: Registering component for domain: " + subdomain);
JID componentJID = new JID(subdomain + "." + serverDomain);
if (routable != null) {
routable.addComponent(component);
}
else {
routable = new RoutableComponents(componentJID, component);
routables.put(subdomain, routable);
// Add the route to the new service provided by the component
XMPPServer.getInstance().getRoutingTable().addComponentRoute(componentJID, routable);
}
// Initialize the new component
try {
component.initialize(componentJID, this);
component.start();
// Notify listeners that a new component has been registered
for (ComponentEventListener listener : listeners) {
listener.componentRegistered(component, componentJID);
}
// Check for potential interested users.
checkPresences();
// Send a disco#info request to the new component. If the component provides information
// then it will be added to the list of discoverable server items.
checkDiscoSupport(component, componentJID);
Log.debug("InternalComponentManager: Component registered for domain: " + subdomain);
}
catch (Exception e) {
// Unregister the component's domain
routable.removeComponent(component);
if (e instanceof ComponentException) {
// Rethrow the exception
throw (ComponentException)e;
}
// Rethrow the exception
throw new ComponentException(e);
}
finally {
if (routable.numberOfComponents() == 0) {
// If there are no more components associated with this subdomain, remove it.
routables.remove(subdomain);
// Remove the route
XMPPServer.getInstance().getRoutingTable().removeComponentRoute(componentJID);
}
}
}
}
/**
* Removes a component. The {@link Component#shutdown} method will be called on the
* component. Note that if the component was an external component that was connected
* several times then all its connections will be terminated.
*
* @param subdomain the subdomain of the component's address.
*/
public void removeComponent(String subdomain) {
List<Component> componentsToRemove = new ArrayList<Component>(routables.get(subdomain).getComponents());
for (Component component : componentsToRemove) {
removeComponent(subdomain, component);
}
}
/**
* Removes a given component. Unlike {@link #removeComponent(String)} this method will just
* remove a single component instead of all components associated to the subdomain. External
* components may connect several times and register for the same subdomain. This method
* just removes a singled connection not all of them.
*
* @param subdomain the subdomain of the component's address.
* @param component specific component to remove.
*/
public void removeComponent(String subdomain, Component component) {
synchronized (routables) {
Log.debug("InternalComponentManager: Unregistering component for domain: " + subdomain);
RoutableComponents routable = routables.get(subdomain);
routable.removeComponent(component);
if (routable.numberOfComponents() == 0) {
routables.remove(subdomain);
// Remove any info stored with the component being removed
componentInfo.remove(subdomain);
JID componentJID = new JID(subdomain + "." + serverDomain);
// Remove the route for the service provided by the component
RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
if (routingTable != null) {
routingTable.removeComponentRoute(componentJID);
}
// Remove the disco item from the server for the component that is being removed
IQDiscoItemsHandler iqDiscoItemsHandler = XMPPServer.getInstance().getIQDiscoItemsHandler();
if (iqDiscoItemsHandler != null) {
iqDiscoItemsHandler.removeComponentItem(componentJID.toBareJID());
}
// Ask the component to shutdown
if (component != null) {
component.shutdown();
}
// Notify listeners that an existing component has been unregistered
for (ComponentEventListener listener : listeners) {
listener.componentUnregistered(component, componentJID);
}
Log.debug("InternalComponentManager: Component unregistered for domain: " + subdomain);
}
else {
Log.debug("InternalComponentManager: Other components still tied to domain: " + subdomain);
}
}
}
public void sendPacket(Component component, Packet packet) {
if (packet != null && packet.getFrom() == null) {
throw new IllegalArgumentException("Packet with no FROM address was received from component.");
}
PacketRouter router = XMPPServer.getInstance().getPacketRouter();
if (router != null) {
router.route(packet);
}
}
public IQ query(Component component, IQ packet, int timeout) throws ComponentException {
final LinkedBlockingQueue<IQ> answer = new LinkedBlockingQueue<IQ>(8);
XMPPServer.getInstance().getIQRouter().addIQResultListener(packet.getID(), new IQResultListener() {
public void receivedAnswer(IQ packet) {
answer.offer(packet);
}
public void answerTimeout(String packetId) {
Log.warn("An answer to a previously sent IQ stanza was never received. Packet id: " + packetId);
}
});
sendPacket(component, packet);
IQ reply = null;
try {
reply = answer.poll(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// Ignore
}
if (reply == null) {
reply = IQ.createResultIQ(packet);
reply.setError(PacketError.Condition.item_not_found);
}
return reply;
}
public void query(Component component, IQ packet, IQResultListener listener) throws ComponentException {
XMPPServer.getInstance().getIQRouter().addIQResultListener(packet.getID(), listener);
sendPacket(component, packet);
}
/**
* Adds a new listener that will be notified of component events. Events being
* notified are: 1) when a component is added to the component manager, 2) when
* a component is deleted and 3) when disco#info is received from a component.
*
* @param listener the new listener to notify of component events.
*/
public void addListener(ComponentEventListener listener) {
listeners.add(listener);
// Notify the new listener about existing components
for (Map.Entry<String, RoutableComponents> entry : routables.entrySet()) {
String subdomain = entry.getKey();
RoutableComponents routable = entry.getValue();
for (Component component : routable.getComponents()) {
JID componentJID = new JID(subdomain + "." + serverDomain);
listener.componentRegistered(component, componentJID);
// Check if there is disco#info stored for the component
IQ disco = componentInfo.get(subdomain);
if (disco != null) {
listener.componentInfoReceived(component, disco);
}
}
}
}
/**
* Removes the specified listener from the listeners being notified of component
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -