Что ж, мне удалось взломать мой путь к решению.
Во-первых, JMockit не очень хорошо работает с JDK13, поэтому я перенес JTP-клиент JDK13 в JDK8. Это злой хак, но этого достаточно для моего тестового случая.
Затем я использовал несколько более старую версию (1.46) JMockit, потому что какой-то комик решил, что никому нельзя позволять подделывать классы с закрытыми методами.
Затем я сохранил объекты, которые я хотел отследить, в List, что позволяет исключить нежелательные объекты из регистрации посредством сравнения идентификаторов (==).
Тем не менее JMockit может обработать sh вкл. некоторые методы (в приведенном ниже примере я их закомментировал), поэтому есть идея подавить ведение журнала после исключения.
Я сообщил об этом людям 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 sr c, длинная позиция, NativeDispatcher nd)
java .nio .DirectByteBuffer.put (ByteBuffer sr c)
DirectByteBuffer использовал некоторые хитрые приемы для считывания моего ByteBuffer.
Решение работает только с моим взломом Http Client, но здесь это в любом случае, просто для записи. Возможно, некоторые из них помогут другим отладить некоторые другие классы:
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());
}
}