From fbe8e16633397a0cc52fcb33d8000cf2047da8bc Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Mon, 2 Mar 2026 10:38:05 -0500 Subject: [PATCH] MLE-27304 Small cleanup for Polaris No change here, just moving InputStreamTee into RequestLoggerImpl as that's the only place where it's used. And it makes it easier to see that the potential thread safety issues of InputStreamTee won't happen in practice. --- .../marklogic/client/impl/InputStreamTee.java | 128 ----------------- .../client/impl/RequestLoggerImpl.java | 131 +++++++++++++++++- 2 files changed, 125 insertions(+), 134 deletions(-) delete mode 100644 marklogic-client-api/src/main/java/com/marklogic/client/impl/InputStreamTee.java diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/InputStreamTee.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/InputStreamTee.java deleted file mode 100644 index 944d7a7ed..000000000 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/InputStreamTee.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. - */ -package com.marklogic.client.impl; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class InputStreamTee extends InputStream { - private InputStream in; - private OutputStream tee; - private long max = 0; - private long sent = 0; - - public InputStreamTee(InputStream in, OutputStream tee, long max) { - super(); - this.in = in; - this.tee = tee; - this.max = max; - } - - @Override - public int read() throws IOException { - if (in == null) return -1; - - if (sent >= max) return in.read(); - - int b = in.read(); - if (b == -1) { - cleanupTee(); - return b; - } - - if (max == Long.MAX_VALUE) { - tee.write(b); - return b; - } - - tee.write(b); - - sent++; - if (sent == max) cleanupTee(); - - return b; - } - @Override - public int read(byte[] b) throws IOException { - if (in == null) return -1; - - if (sent >= max) return in.read(b); - - return readTee(b, 0, in.read(b)); - } - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (in == null) return -1; - - if (sent >= max) return in.read(b, off, len); - - return readTee(b, 0, in.read(b, off, len)); - } - private int readTee(byte[] b, int off, int resultLen) throws IOException { - if (resultLen < 1) { - if (resultLen == -1) cleanupTee(); - return resultLen; - } - - if (max == Long.MAX_VALUE) { - tee.write(b, off, resultLen); - return resultLen; - } - - int teeLen = ((sent + resultLen) <= max) ? resultLen : (int) (max - sent); - sent += teeLen; - tee.write(b, off, teeLen); - - if (sent >= max) cleanupTee(); - - return resultLen; - } - private void cleanupTee() throws IOException { - if (tee == null) return; - - tee.flush(); - tee = null; - } - @Override - public void close() throws IOException { - if (in == null) return; - - in.close(); - in = null; - - cleanupTee(); - } - - @Override - public int available() throws IOException { - if (in == null) return 0; - - return in.available(); - } - @Override - public boolean markSupported() { - if (in == null) return false; - - return in.markSupported(); - } - @Override - public synchronized void mark(int readlimit) { - if (in == null) return; - - in.mark(readlimit); - } - @Override - public synchronized void reset() throws IOException { - if (in == null) throw new IOException("Input Stream closed"); - - in.reset(); - } - @Override - public long skip(long n) throws IOException { - if (in == null) throw new IOException("Input Stream closed"); - - return in.skip(n); - } -} diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/RequestLoggerImpl.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/RequestLoggerImpl.java index e585df7c9..941dc5449 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/RequestLoggerImpl.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/RequestLoggerImpl.java @@ -1,13 +1,9 @@ /* - * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + * Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ package com.marklogic.client.impl; -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.Reader; +import java.io.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,4 +113,127 @@ public void close() { enabled = false; } + // Moved here in the 8.2 release to make it obvious it's only used by RequestLoggerImpl. It is only used by the + // copyContent method in RequestLoggerImpl. So while Polaris rightfully complains about the thread safety issues of + // this class, it is only used in a single thread context and so the thread safety issues are not a problem. + private static class InputStreamTee extends InputStream { + private InputStream in; + private OutputStream tee; + private long max = 0; + private long sent = 0; + + public InputStreamTee(InputStream in, OutputStream tee, long max) { + super(); + this.in = in; + this.tee = tee; + this.max = max; + } + + @Override + public int read() throws IOException { + if (in == null) return -1; + + if (sent >= max) return in.read(); + + int b = in.read(); + if (b == -1) { + cleanupTee(); + return b; + } + + if (max == Long.MAX_VALUE) { + tee.write(b); + return b; + } + + tee.write(b); + + sent++; + if (sent == max) cleanupTee(); + + return b; + } + + @Override + public int read(byte[] b) throws IOException { + if (in == null) return -1; + + if (sent >= max) return in.read(b); + + return readTee(b, 0, in.read(b)); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (in == null) return -1; + + if (sent >= max) return in.read(b, off, len); + + return readTee(b, 0, in.read(b, off, len)); + } + + private int readTee(byte[] b, int off, int resultLen) throws IOException { + if (resultLen < 1) { + if (resultLen == -1) cleanupTee(); + return resultLen; + } + + if (max == Long.MAX_VALUE) { + tee.write(b, off, resultLen); + return resultLen; + } + + int teeLen = ((sent + resultLen) <= max) ? resultLen : (int) (max - sent); + sent += teeLen; + tee.write(b, off, teeLen); + + if (sent >= max) cleanupTee(); + + return resultLen; + } + + private void cleanupTee() throws IOException { + if (tee == null) return; + tee.flush(); + tee = null; + } + + @Override + public void close() throws IOException { + if (in == null) return; + in.close(); + in = null; + cleanupTee(); + } + + @Override + public int available() throws IOException { + if (in == null) return 0; + return in.available(); + } + + @Override + public boolean markSupported() { + if (in == null) return false; + return in.markSupported(); + } + + @Override + public synchronized void mark(int readLimit) { + if (in == null) return; + in.mark(readLimit); + } + + @Override + public synchronized void reset() throws IOException { + if (in == null) throw new IOException("Input Stream closed"); + in.reset(); + } + + @Override + public long skip(long n) throws IOException { + if (in == null) throw new IOException("Input Stream closed"); + return in.skip(n); + } + } }