@Override
        public void run()
        {
            if (!isWebSocketOpen() || getMaxIdleTime().isNegative() || getMaxIdleTime().isZero()) {
                TaskEngine.getInstance().cancelScheduledTask(websocketFramePingTask);
                TaskEngine.getInstance().cancelScheduledTask(xmppSessionIdleTask);
                return;
            }

            // Was there activity since we've pinged, then reset the 'ping' status.
            if (pendingPingSentAt != null && lastReceived.isAfter(pendingPingSentAt)) {
                pendingPingSentAt = null;
            }

            final Duration inactive = Duration.between(lastReceived, Instant.now());
            if (inactive.compareTo(getMaxIdleTime()) > 0) {
                // Inactive session, that didn't respond to a ping that should've been sent earlier. Kill.
                Log.debug("Closing connection that has been idle: {}", wsConnection);
                wsConnection.close(new StreamError(StreamError.Condition.connection_timeout, "Closing connection due to inactivity."));
                return;
            }

            final boolean doPing = ConnectionSettings.Client.KEEP_ALIVE_PING_PROPERTY.getValue();
            if (doPing) {
                // Probe the peer once, when it's inactive for 50% of the maximum idle time (this mimics the behavior for TCP connections).
                final boolean alreadyPinged = pendingPingSentAt != null;
                if (!alreadyPinged) {
                    if (inactive.compareTo(getMaxIdleTime().dividedBy(2)) > 0) {
                        // Client has been inactive for more than 50% of the max idle time, and has not been pinged yet.
                        sendPing();
                    }
                }
            }
        }
Task that, on prolonged inactivity, sends an XMPP ping, to ensure that the remote entity is still responsive.
xmppserver/src/main/java/org/jivesoftware/openfire/websocket/WebSocketClientConnectionHandler.java