private InputStream createInputStream() {
        return new InputStream() {
            @Override
            public synchronized int read() throws IOException {
                doRead();
                if (!inAppBB.hasRemaining()) {
                    return -1;
                }
                return inAppBB.get();
            }

            @Override
            public synchronized int read(byte[] bytes, int off, int len) throws IOException {
                // Check if in the previous read the inAppBB ByteBuffer remained with unread data.
                // If all the data was consumed then read from the socket channel. Otherwise,
                // consume the data contained in the buffer.
                if (inAppBB.position() == 0) {
                    // Read from the channel the encrypted data, decrypt it and load it
                    // into inAppBB
                    doRead();
                }
                else {
                    //System.out.println("#createInputStream. Detected previously unread data. position: " + inAppBB.position());

                    // The inAppBB contains data from a previous read so set the position to 0
                    // to consume it
                    inAppBB.flip();
                }
                len = Math.min(len, inAppBB.remaining());
                if (len == 0) {
                    // Nothing was read so the end of stream should have been reached.
                    return -1;
                }
                inAppBB.get(bytes, off, len);
                // If the requested length is less than the limit of inAppBB then all the data
                // inside inAppBB was not read. In that case we need to discard the read data and
                // keep only the unread data to be consume the next time this method is called
                if (inAppBB.hasRemaining()) {
                    // Discard read data and move unread data to the begining of the buffer. Leave
                    // the position at the end of the buffer as a way to indicate that there is
                    // unread data
                    inAppBB.compact();

                    //System.out.println("#createInputStream. Data left unread. inAppBB compacted. position: " + inAppBB.position() + " limit: " + inAppBB.limit());
                }
                else {
                    // Everything was read so reset the buffer
                    inAppBB.clear();
                }
                return len;
            }
        };
    }
<code>inAppBB</code> buffer keeps decypted data.
xmppserver/src/main/java/org/jivesoftware/openfire/net/TLSStreamReader.java