public int read(byte[] abData, int nOffset, int nLength) {
        LOG.log(Level.FINE, ">TCircularBuffer.read(): called.");
        dumpInternalState();
        if (!isOpen()) {
            if (availableRead() > 0) {
                nLength = Math.min(nLength, availableRead());
                LOG.log(Level.FINE, "reading rest in closed buffer, length: {0}", nLength);
            } else {
                LOG.log(Level.FINE, "< not open. returning -1.");
                return -1;
            }
        }
        synchronized (this) {
            if (m_trigger != null && availableRead() < nLength) {
                LOG.log(Level.FINE, "executing trigger.");
                m_trigger.dataReady();
            }
            if (!m_bBlockingRead) {
                nLength = Math.min(availableRead(), nLength);
            }
            int nRemainingBytes = nLength;
            while (nRemainingBytes > 0) {
                while (availableRead() == 0) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        LOG.log(Level.FINE, e.getMessage());
                    }
                }
                int nAvailable = Math.min(availableRead(), nRemainingBytes);
                while (nAvailable > 0) {
                    int nToRead = Math.min(nAvailable, m_nSize - getReadPos());
                    System.arraycopy(m_abData, getReadPos(), abData, nOffset, nToRead);
                    m_lReadPos += nToRead;
                    nOffset += nToRead;
                    nAvailable -= nToRead;
                    nRemainingBytes -= nToRead;
                }
                notifyAll();
            }
            LOG.log(Level.FINE, "After read:");
            // dumpInternalState();
            LOG.log(Level.FINE, "< completed. Read {0} bytes", nLength);
            return nLength;
        }
    }