class LocalExternalComponent implements ComponentSession.ExternalComponent {
        /**
         * Keeps track of the IQ (get/set) packets that were sent from a given component's connection. This
         * information will be used to ensure that the IQ reply will be sent to the same component's connection.
         */
        private static final Map<String, LocalExternalComponent> iqs = new HashMap<>();

        private LocalComponentSession session;
        private Connection connection;
        private String name = "";
        private String type = "";
        private String category = "";
        /**
         * List of subdomains that were binded for this component. The list will include
         * the initial subdomain.
         */
        private List<String> subdomains = new ArrayList<>();


        public LocalExternalComponent(LocalComponentSession session, Connection connection) {
            this.session = session;
            this.connection = connection;
        }

        @Override
        public void processPacket(Packet packet) {<FILL_FUNCTION_BODY>}

        /**
         * Delivers the packet to the external component.
         *
         * @param packet the packet to deliver.
         */
        void deliver(Packet packet) {
            if (connection != null && !connection.isClosed()) {
                try {
                    connection.deliver(packet);
                }
                catch (Exception e) {
                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
                    connection.close(new StreamError(StreamError.Condition.internal_server_error));
                }
            }
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getDescription() {
            return category + " - " + type;
        }

        @Override
        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String getType() {
            return type;
        }

        @Override
        public void setType(String type) {
            this.type = type;
        }

        @Override
        public String getCategory() {
            return category;
        }

        @Override
        public void setCategory(String category) {
            this.category = category;
        }

        @Override
        public String getInitialSubdomain() {
            if (subdomains.isEmpty()) {
                return null;
            }
            return subdomains.get(0);
        }

        private void addSubdomain(String subdomain) {
            subdomains.add(subdomain);
        }

        @Override
        public Collection<String> getSubdomains() {
            return subdomains;
        }

        @Override
        public void initialize(JID jid, ComponentManager componentManager) {
            addSubdomain(jid.toString());
        }

        @Override
        public void start() {
        }

        @Override
        public void shutdown() {
            // Remove tracking of IQ packets sent from this component
            synchronized (iqs) {
                List<String> toRemove = new ArrayList<>();
                for (Map.Entry<String,LocalExternalComponent> entry : iqs.entrySet()) {
                    if (entry.getValue() == this) {
                        toRemove.add(entry.getKey());
                    }
                }
                // Remove keys pointing to component being removed
                for (String key : toRemove) {
                    iqs.remove(key);
                }
            }
        }

        @Override
        public String toString() {
            return super.toString() + " - subdomains: " + subdomains;
        }

        public void track(IQ iq) {
            synchronized (iqs) {
                iqs.put(iq.getID(), this);
            }
        }
        /**
         * @return the session
         */
        public LocalComponentSession getSession() {
            return session;
        }
    }

            if (packet instanceof IQ) {
                IQ iq = (IQ) packet;
                if (iq.getType() == IQ.Type.result || iq.getType() == IQ.Type.error) {
                    // Check if this IQ reply belongs to a specific component and route
                    // reply to that specific component (if it exists)
                    LocalExternalComponent targetComponent;
                    synchronized (iqs) {
                        targetComponent = iqs.remove(packet.getID());
                    }
                    if (targetComponent != null) {
                        targetComponent.processPacket(packet);
                        return;
                    }
                }
            }
            // Ask the session to process the outgoing packet. This will
            // give us the chance to apply PacketInterceptors
            session.process(packet);
        
<methods>public void <init>(java.lang.String, org.jivesoftware.openfire.Connection, org.jivesoftware.openfire.StreamID, java.util.Locale) ,public void close() ,public void deliverRawText(java.lang.String) ,public JID getAddress() ,public abstract List<Element> getAvailableStreamFeatures() ,public java.lang.String getCipherSuiteName() ,public org.jivesoftware.openfire.Connection getConnection() ,public java.util.Date getCreationDate() ,public java.lang.String getHostAddress() throws java.net.UnknownHostException,public java.lang.String getHostName() throws java.net.UnknownHostException,public final java.util.Locale getLanguage() ,public java.util.Date getLastActiveDate() ,public long getNumClientPackets() ,public long getNumServerPackets() ,public java.security.cert.Certificate[] getPeerCertificates() ,public java.lang.String getServerName() ,public java.lang.Object getSessionData(java.lang.String) ,public Map<java.lang.String,java.lang.String> getSoftwareVersion() ,public org.jivesoftware.openfire.session.Session.Status getStatus() ,public org.jivesoftware.openfire.StreamID getStreamID() ,public org.jivesoftware.openfire.streammanagement.StreamManager getStreamManager() ,public java.lang.String getTLSProtocolName() ,public void incrementClientPacketCount() ,public void incrementServerPacketCount() ,public boolean isClosed() ,public boolean isDetached() ,public boolean isEncrypted() ,public boolean isSecure() ,public boolean isUsingSelfSignedCertificate() ,public void process(Packet) ,public void reattach(org.jivesoftware.openfire.session.LocalSession, long) ,public java.lang.Object removeSessionData(java.lang.String) ,public void setAddress(JID) ,public void setDetached() ,public java.lang.Object setSessionData(java.lang.String, java.lang.Object) ,public void setSoftwareVersionData(java.lang.String, java.lang.String) ,public void setStatus(org.jivesoftware.openfire.session.Session.Status) ,public java.lang.String toString() ,public boolean validate() <variables>private static final Logger Log,protected JID address,private java.util.concurrent.atomic.AtomicLong clientPacketCount,protected org.jivesoftware.openfire.Connection conn,private final non-sealed java.util.Locale language,private long lastActiveDate,private final java.util.concurrent.locks.Lock lock,protected final non-sealed java.lang.String serverName,private java.util.concurrent.atomic.AtomicLong serverPacketCount,private final Map<java.lang.String,java.lang.Object> sessionData,protected org.jivesoftware.openfire.SessionManager sessionManager,private Map<java.lang.String,java.lang.String> softwareVersionData,protected final long startDate,protected org.jivesoftware.openfire.session.Session.Status status,protected final non-sealed org.jivesoftware.openfire.StreamID streamID,protected final non-sealed org.jivesoftware.openfire.streammanagement.StreamManager streamManager