private void finishOutboundJoin()
    {
        if ( outboundJoinProgress == null ) {
            throw new IllegalStateException( "Cannot finish outbound join from '" + room.getJID() + "' as none is in progress." );
        }
        Log.trace("(room: '{}'): Finish setting up the outbound FMUC join with '{}'.", room.getJID(), outboundJoinProgress.getPeer() );
        if ( !outboundJoinProgress.isJoinComplete() ) {
            throw new IllegalStateException( "Cannot finish outbound join from '" + room.getJID()+"' to '"+ outboundJoinProgress.getPeer()+"', as it is not complete!" );
        }

        List<OutboundJoinProgress.QueuedStanza> queued = outboundJoinProgress.purgeQueue();
        if ( outboundJoinProgress.isRejected() )
        {
            Log.trace("(room: '{}'): Notifying callback waiting for the complete FMUC join response from '{}' with a rejection.", room.getJID(), outboundJoinProgress.getPeer() );
            final FMUCException rejection = new FMUCException(outboundJoinProgress.getRejectionMessage());
            outboundJoinProgress.getCallback().completeExceptionally(rejection);
            queued.forEach( queuedStanza -> queuedStanza.future.completeExceptionally(rejection));
        }
        else
        {
            Log.trace("(room: '{}'): Synchronizing state of local room with joined FMUC node '{}'.", room.getJID(), outboundJoinProgress.getPeer() );
            outboundJoin = new OutboundJoin(outboundJoinConfiguration);

            // Before processing the data in context of the local FMUC room, ensure that the FMUC metadata state is up-to-date.
            for ( final Packet response : outboundJoinProgress.getResponses() ) {
                if ( response instanceof Presence ) {
                    final JID occupantOnJoinedNode = getFMUCFromJID(response);
                    outboundJoin.addOccupant( occupantOnJoinedNode );
                }
            }

            // Use a room role that can be used to identify the remote fmuc node (to prevent data from being echo'd back)
            final MUCOccupant occupantData = MUCOccupant.createRoomSelfRepresentation(room);
            occupantData.setReportedFmucAddress( outboundJoin.getPeer() );

            // Use received data to augment state of the local room.
            for ( final Packet response : outboundJoinProgress.getResponses() ) {
                try
                {
                    final List<Message> history = new LinkedList<>();
                    if ( response instanceof Presence ) {
                        makeRemoteOccupantJoinRoom((Presence) response);
                    } else if ( response instanceof Message && response.getElement().element("body") != null) {
                        history.add((Message) response);
                    } else if ( response instanceof Message && response.getElement().element("subject") != null) {
                        // When the subject is received, all historic messages must have already been received.
                        addRemoteHistoryToRoom(history);
                        applyRemoteSubjectToRoom((Message) response, occupantData);
                    }
                } catch ( Exception e ) {
                    Log.error( "(room: '{}'): An unexpected exception occurred while processing FMUC join response stanzas.", room.getJID(), e );
                }
            }

            Log.trace("(room: '{}'): Notifying callback waiting for the complete FMUC join response from '{}' with success.", room.getJID(), outboundJoinProgress.getPeer() );
            outboundJoinProgress.getCallback().complete( null );

            Log.trace("(room: '{}'): Sending {} stanza(s) that were queued, waiting for the complete FMUC join", room.getJID(), queued );
            for ( final OutboundJoinProgress.QueuedStanza queuedStanza : queued ) {
                try {
                    doPropagateOutbound(queuedStanza.stanza, queuedStanza.sender, queuedStanza.future);
                } catch ( Exception e ) {
                    Log.warn( "An exception occurred while trying to process a stanza that was queued pending completion of FMUC join in room " + room.getJID() + ": " + queuedStanza.stanza );
                }
            }
        }
    }
Processes a stanza that is received from another node in the FMUC set, by translating it into 'regular' MUC data.

The provided input is expected to be a stanza received from another node in the FMUC set. It is stripped from
FMUC data, after which it is distributed to the local users.

Additionally, it is sent out to all (other) FMUC nodes that are known.

@param stanza The data to be processed.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/FMUCHandler.java