/** * Processes packets that were sent to this service. Currently only packets that were sent from * registered components are being processed. In the future, we may also process packet of trusted * clients. Trusted clients may be able to execute ad-hoc commands such as adding or removing * components. * * @param packet the packet to process. */ @Override public void process(Packet packet) throws PacketException { List<Component> components = getComponents(packet.getFrom()); // Only process packets that were sent by registered components if (!components.isEmpty()) { if (packet instanceof IQ && IQ.Type.result == ((IQ) packet).getType()) { IQ iq = (IQ) packet; Element childElement = iq.getChildElement(); if (childElement != null) { String namespace = childElement.getNamespaceURI(); if ("http://jabber.org/protocol/disco#info".equals(namespace)) { // Add a disco item to the server for the component that supports disco Element identity = childElement.element("identity"); if (identity == null) { // Do nothing since there are no identities in the disco#info packet return; } try { XMPPServer.getInstance() .getIQDiscoItemsHandler() .addComponentItem(packet.getFrom().toBareJID(), identity.attributeValue("name")); for (Component component : components) { if (component instanceof ComponentSession.ExternalComponent) { ComponentSession.ExternalComponent externalComponent = (ComponentSession.ExternalComponent) component; externalComponent.setName(identity.attributeValue("name")); externalComponent.setType(identity.attributeValue("type")); externalComponent.setCategory(identity.attributeValue("category")); } } } catch (Exception e) { Log.error( "Error processing disco packet of components: " + components + " - " + packet.toXML(), e); } // Store the IQ disco#info returned by the component addComponentInfo(iq); // Notify listeners that a component answered the disco#info request notifyComponentInfo(iq); // Alert other cluster nodes CacheFactory.doClusterTask(new NotifyComponentInfo(iq)); } } } } }
/** * Handles packets that includes a data form. The data form was sent using an element with name * "x" and namespace "jabber:x:data". * * @param senderRole the role of the user that sent the data form. * @param formElement the element that contains the data form specification. * @throws ForbiddenException if the user does not have enough privileges. * @throws ConflictException If the room was going to lose all of its owners. */ private void handleDataFormElement(MUCRole senderRole, Element formElement) throws ForbiddenException, ConflictException { DataForm completedForm = new DataForm(formElement); switch (completedForm.getType()) { case cancel: // If the room was just created (i.e. is locked) and the owner cancels the configuration // form then destroy the room if (room.isLocked()) { room.destroyRoom(null, null); } break; case submit: // The owner is requesting an instant room if (completedForm.getFields().isEmpty()) { // Do nothing } // The owner is requesting a reserved room or is changing the current configuration else { processConfigurationForm(completedForm, senderRole); } // If the room was locked, unlock it and send to the owner the "room is now unlocked" // message if (room.isLocked() && !room.isManuallyLocked()) { room.unlock(senderRole); } if (!room.isDestroyed) { // Let other cluster nodes that the room has been updated CacheFactory.doClusterTask(new RoomUpdatedEvent(room)); } break; default: Log.warn("cannot handle data form element: " + formElement.asXML()); break; } }
/** * 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) { if (component == null) { return; } 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); JID componentJID = new JID(subdomain + "." + serverDomain); // Remove the route for the service provided by the component routingTable.removeComponentRoute(componentJID); // Ask the component to shutdown component.shutdown(); if (!routingTable.hasComponentRoute(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()); } removeComponentInfo(componentJID); // Notify listeners that an existing component has been unregistered notifyComponentUnregistered(componentJID); // Alert other nodes of component removed event CacheFactory.doClusterTask(new NotifyComponentUnregistered(componentJID)); } Log.debug("InternalComponentManager: Component unregistered for domain: " + subdomain); } else { Log.debug("InternalComponentManager: Other components still tied to domain: " + subdomain); } } }
@Override 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); boolean notifyListeners = false; if (routable != null) { routable.addComponent(component); } else { routable = new RoutableComponents(componentJID, component); routables.put(subdomain, routable); if (!routingTable.hasComponentRoute(componentJID)) { notifyListeners = true; } // Add the route to the new service provided by the component routingTable.addComponentRoute(componentJID, routable); } // Initialize the new component try { component.initialize(componentJID, this); component.start(); if (notifyListeners) { // Notify listeners that a new component has been registered notifyComponentRegistered(componentJID); // Alert other nodes of new registered domain event CacheFactory.doClusterTask(new NotifyComponentRegistered(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); } } } }