@Override
    public void processPacket(final Packet packet) {

        Log.trace( "Routing stanza: {}", packet.toXML() );
        if (!isServiceEnabled()) {
            Log.trace( "Service is disabled. Ignoring stanza." );
            return;
        }
        // The MUC service will receive all the packets whose domain matches the domain of the MUC
        // service. This means that, for instance, a disco request should be responded by the
        // service itself instead of relying on the server to handle the request.
        try {
            // Check for IQ Ping responses
            if (isPendingPingResponse(packet)) {
                Log.debug("Ping response received from occupant '{}', addressed to: '{}'", packet.getFrom(), packet.getTo());
                return;
            }
            // Check for 'ghost' users (OF-910 / OF-2209 / OF-2369)
            if (isDeliveryRelatedErrorResponse(packet)) {
                Log.info("Received a stanza that contained a delivery-related error response from {}. This is indicative of a 'ghost' user. Removing this user from all chat rooms.", packet.getFrom());
                removeChatUser(packet.getFrom());
                return;
            }
            // Check if the packet is a disco request or a packet with namespace iq:register
            if (packet instanceof IQ) {
                if (process((IQ)packet)) {
                    Log.trace( "Done processing IQ stanza." );
                    return;
                }
            }

            if ( packet.getTo().getNode() == null )
            {
                Log.trace( "Stanza was addressed at the service itself, which by now should have been handled." );
                if ( packet instanceof IQ && ((IQ) packet).isRequest() )
                {
                    final IQ reply = IQ.createResultIQ( (IQ) packet );
                    reply.setChildElement( ((IQ) packet).getChildElement().createCopy() );
                    reply.setError( PacketError.Condition.feature_not_implemented );
                    XMPPServer.getInstance().getPacketRouter().route( reply );
                }
                Log.debug( "Ignoring stanza addressed at conference service: {}", packet.toXML() );
            }
            else
            {
                Log.trace( "The stanza is a normal packet that should possibly be sent to the room." );
                final JID recipient = packet.getTo();
                final String roomName = recipient != null ? recipient.getNode() : null;
                final JID userJid = packet.getFrom();
                occupantManager.registerActivity(userJid);
                Log.trace( "Stanza recipient: {}, room name: {}, sender: {}", recipient, roomName, userJid );
                if ( !packet.getElement().elements(FMUCHandler.FMUC).isEmpty() ) {
                    Log.trace( "Stanza is a FMUC stanza." );
                    if (roomName == null) {
                        Log.warn("Unable to process FMUC stanza, as it does not address a room: {}", packet.toXML());
                    } else {
                        final Lock lock = getChatRoomLock(roomName);
                        lock.lock();
                        try {
                            final MUCRoom chatRoom = getChatRoom(roomName);
                            if (chatRoom != null) {
                                chatRoom.getFmucHandler().process(packet);
                                // Ensure that other cluster nodes see the changes applied by the method above.
                                syncChatRoom(chatRoom);
                            } else {
                                Log.warn("Unable to process FMUC stanza, as room it's addressed to does not exist: {}", roomName);
                                // FIXME need to send error back in case of IQ request, and FMUC join. Might want to send error back in other cases too.
                            }
                        } finally {
                            lock.unlock();
                        }
                    }
                } else if (IQMUCvCardHandler.PROPERTY_ENABLED.getValue() && packet instanceof IQ && ((IQ) packet).isResponse() && (((IQ) packet).getChildElement() != null) && ((IQ) packet).getChildElement().getNamespaceURI().equals(IQMUCvCardHandler.NAMESPACE)) {
                    Log.trace( "Stanza is a VCard response stanza." );
                    processVCardResponse((IQ) packet);
                } else {
                    Log.trace( "Stanza is a regular MUC stanza." );
                    processRegularStanza(packet);
                }
            }
        }
        catch (final Exception e) {
            Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
        }
    }
Create a new group chat server.

@param subdomain
           Subdomain portion of the conference services (for example,
           conference for conference.example.org)
@param description
           Short description of service for disco and such. If
           {@code null} or empty, a default value will be used.
@param isHidden
           True if this service should be hidden from services views.
@throws IllegalArgumentException
            if the provided subdomain is an invalid, according to the JID
            domain definition.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java