在 Java 系统类中记录方法调用

戴夫丹麦人

我正在寻找一种方法来记录对java.nio.ByteBuffer.

我只想知道正在调用哪些方法。

JMockit 可以做到这一点,但是从 1.47 版开始,一些非常聪明的人决定取消对私有方法的支持,而 1.46 版在 JDK 9 及更高版本上运行得不太好。

谁能推荐一个工具?它不一定需要是一个单元测试框架,但它应该在 Eclipse 中工作。

我至少需要支持 JDK 11(最好是 JDK 13)

仅作记录,以下是适用于 JMockit 1.46 和 JDK 1.8 的代码:

import java.nio.ByteBuffer;
import org.junit.Test;
import org.slf4j.*;
import mockit.*;

public class TestFakeByteBufferAdvice {

    private static final Logger LOG = LoggerFactory.getLogger(TestFakeByteBufferAdvice.class);

    public static final class FakeByteBuffer extends MockUp<ByteBuffer> {
        @Mock
        public Object $advice(Invocation invocation) {
            LOG.info("$advice.....: {} {}", invocation.getInvokedMember(), invocation);

            return invocation.proceed();
        }
    }

    @Test
    public void getFakeByteBuffer() {

        final ByteBuffer real  = ByteBuffer.wrap("abc".getBytes());
        LOG.info("Real........: {} {}", real, real.array());

        LOG.info("MockUp......: {}",    new FakeByteBuffer());

        final ByteBuffer fake  = ByteBuffer.wrap("def".getBytes());
        LOG.info("Fake........: {} {}", fake, fake.array());
    }
}
戴夫丹麦人

好吧,我设法破解了解决方案。

首先,JMockit 不能很好地与 JDK13 配合使用,所以我将 JDK13 Http Client 向后移植到 JDK8。它是一个邪恶的黑客,但对于我的测试用例来说已经足够了。

然后我使用了一个稍旧的 JMockit 版本 (1.46),因为一些喜剧演员决定不允许任何人使用私有方法伪造类。

然后我将我想要跟踪的对象存储在一个列表中,这使得可以通过标识 (==) 比较从日志记录中排除不需要的对象。

仍然 JMockit 可能会在某些方法上崩溃(在下面的示例中,我已经将它们注释掉了),因此在异常之后抑制日志记录是一个想法。
我已将此报告给 JMockit 人员:https : //github.com/jmockit/jmockit1/issues/667

类似地,在构建测试用例以保持输出量减少时抑制日志记录是有意义的。我为此使用了 AtomicBoolean。

跟踪仍然没有完全解决它,但是在 Stacktrace 中抛出让我找到了解决方案:以下调用链正在读取我的 ByteBuffers:
sun.nio.ch.write(ByteBuffer[] srcs, int offset, int length)
sun.nio.ch.write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd)
java.nio.DirectByteBuffer.put(ByteBuffer src)

DirectByteBuffer 使用了一些聪明的技巧来读取我的 ByteBuffer。

该解决方案仅适用于我的 Http Client hack,但无论如何,这里只是为了记录。也许其中一些会帮助其他人调试一些其他类:

package http.jmockit;

import java.net.*;
import java.net.http.*;
import java.net.http.HttpRequest.BodyPublisher;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.*;
import mockit.*;

public class TestHttpIdentity {

    private static final Logger        LOG         = LoggerFactory.getLogger(TestHttpIdentity.class);
    private static final AtomicBoolean LOG_ENABLED = new AtomicBoolean();
    private static final List<Object>  TRACKED     = new ArrayList<>();

    public static final class FakeByteBuffer extends MockUp<ByteBuffer> {
        @Mock
        public Object $advice(final Invocation invocation) {

            if (TRACKED.stream().noneMatch(tracked -> tracked == invocation.getInvokedInstance())) {
                return invocation.proceed();
            }

            if (LOG_ENABLED.get()) {
                LOG    .info("$advice.invokedInstance.: {}", invocation.getInvokedInstance(), "" /* (makes signature unique)*/);
                LOG    .info("$advice.invokedMember...: {}", invocation.getInvokedMember(),   "" /* (makes signature unique)*/);
//              Thread.dumpStack(); // Use Stack Trace as last measure if needs be
            }

            Object     result = "Not available due to Exception in invocation.proceed()";
            try {
                /**/   result = invocation.proceed();
                return result;
            }
            catch (final Throwable e) {

                for (final Object arg : invocation.getInvokedArguments()) {
                    LOG.info("$advice.arg.............: {} class={}", arg,   arg == null ? "?" : arg.getClass());
                }
                LOG    .info("$advice.Result..........: {}", result);

                LOG_ENABLED.set(false);  // Disable Logging when JMockit fails

                try {Thread.sleep(100);} catch (final InterruptedException shortDelayToSyncLoggingOutput) {}

                e.printStackTrace();
                throw e;
            }
        }
    }

    public static void main(final String[] args) throws Exception {

        LOG.info("MockUp..................: {}", new FakeByteBuffer());

        final ByteBuffer[] byteBuffers  = TestBytes.asWrappedByteBuffers();

        for (final ByteBuffer byteBuffer : byteBuffers) {
            LOG.info("byteBuffer..............: {}", byteBuffer);

            final int limit    = byteBuffer.limit();
            final int position = byteBuffer.position();

            TRACKED.add(byteBuffer);  // Track Objects via their Identity (==)

            LOG.info("Test Bytes..............: {}", byteBuffers, "");
            LOG.info("byteBuffer0.array().....: {}", byteBuffer.array());
            LOG.info("byteBuffer0.capacity()..: {}", byteBuffer.capacity());
            LOG.info("byteBuffer0.get().......: {}", byteBuffer.get());
//          LOG.info("byteBuffer0.get(byte[]).: {}", byteBuffers0.get(new byte[5]));  // ClassCastException
            LOG.info("byteBuffer0.get(byte[]->) {}", byteBuffer.get(new byte[5], 0, 5));
            LOG.info("byteBuffer0.get(0)......: {}", byteBuffer.get(0));
            LOG.info("byteBuffer0.hasArray()..: {}", byteBuffer.hasArray());
            LOG.info("byteBuffer0.hasRemaining: {}", byteBuffer.hasRemaining());
            LOG.info("byteBuffer0.isDirect()..: {}", byteBuffer.isDirect());
            LOG.info("byteBuffer0.isReadOnly(): {}", byteBuffer.isReadOnly());
            LOG.info("byteBuffer0.limit().....: {}", limit);
            LOG.info("byteBuffer0.limit(0)....: {}", byteBuffer.limit(limit));
            LOG.info("byteBuffer0.mark(0).....: {}", byteBuffer.mark());
            LOG.info("byteBuffer0.order().....: {}", byteBuffer.order());
            LOG.info("byteBuffer0.position()..: {}", position);
            LOG.info("byteBuffer0.position(99): {}", byteBuffer.position(99));
            LOG.info("byteBuffer0.remaining().: {}", byteBuffer.remaining());
//          LOG.info("byteBuffer0.reset().....: {}", byteBuffers0.reset());      // -> InvalidMarkException
            LOG.info("byteBuffer0.rewind()....: {}", byteBuffer.rewind());
            LOG.info("byteBuffer0.slice().....: {}", byteBuffer.slice());

            byteBuffer.rewind();
            byteBuffer.position(position);
            byteBuffer.limit   (limit);

            LOG.info("byteBuffer..............: {}", byteBuffer);
        }
        final BodyPublisher pub = new ByteArrayBodyPublisherIterator(byteBuffers);

        LOG_ENABLED.set(false);  // Enable Logging now we've got things set up.

        final HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://localhost:631"))
                .headers("Content-Type", "application/ipp")
                .POST(pub)
                .build();

        HttpResponse<byte[]> response = HttpClient
                .newBuilder()
                .build()
                .send(request, HttpResponse.BodyHandlers.ofByteArray());

        LOG.info("Result......: {} {}", response, response.body());
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章