Merge pull request #4558 from eclipse/jetty-10.0.x-3924-deprecate-resource-writeto
Issue #3924 - Remove Resource.writeTo(OutputStream, long, long)
This commit is contained in:
commit
a1c20b6cfa
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
|
|||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
|
@ -46,9 +47,11 @@ import org.eclipse.jetty.http.QuotedCSV;
|
|||
import org.eclipse.jetty.http.QuotedQualityCSV;
|
||||
import org.eclipse.jetty.io.WriterOutputStream;
|
||||
import org.eclipse.jetty.server.resource.HttpContentRangeWriter;
|
||||
import org.eclipse.jetty.server.resource.InputStreamRangeWriter;
|
||||
import org.eclipse.jetty.server.resource.RangeWriter;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.MultiPartOutputStream;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -669,18 +672,14 @@ public class ResourceService
|
|||
if (include)
|
||||
{
|
||||
// write without headers
|
||||
content.getResource().writeTo(out, 0, content_length);
|
||||
writeContent(content, out, 0, content_length);
|
||||
}
|
||||
// else if we can't do a bypass write because of wrapping
|
||||
else if (written || !(out instanceof HttpOutput))
|
||||
{
|
||||
// write normally
|
||||
putHeaders(response, content, written ? -1 : 0);
|
||||
ByteBuffer buffer = content.getIndirectBuffer();
|
||||
if (buffer != null)
|
||||
BufferUtil.writeTo(buffer, out);
|
||||
else
|
||||
content.getResource().writeTo(out, 0, content_length);
|
||||
writeContent(content, out, 0, content_length);
|
||||
}
|
||||
// else do a bypass write
|
||||
else
|
||||
|
@ -753,7 +752,7 @@ public class ResourceService
|
|||
response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
|
||||
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
||||
singleSatisfiableRange.toHeaderRangeString(content_length));
|
||||
content.getResource().writeTo(out, singleSatisfiableRange.getFirst(), singleLength);
|
||||
writeContent(content, out, singleSatisfiableRange.getFirst(), singleLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -815,6 +814,33 @@ public class ResourceService
|
|||
return true;
|
||||
}
|
||||
|
||||
private static void writeContent(HttpContent content, OutputStream out, long start, long contentLength) throws IOException
|
||||
{
|
||||
// Is the write for the whole content?
|
||||
if (start == 0 && content.getResource().length() == contentLength)
|
||||
{
|
||||
// attempt efficient ByteBuffer based write for whole content
|
||||
ByteBuffer buffer = content.getIndirectBuffer();
|
||||
if (buffer != null)
|
||||
{
|
||||
BufferUtil.writeTo(buffer, out);
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream input = content.getResource().getInputStream())
|
||||
{
|
||||
IO.copy(input, out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a ranged writer
|
||||
try (InputStreamRangeWriter rangeWriter = new InputStreamRangeWriter(() -> content.getInputStream()))
|
||||
{
|
||||
rangeWriter.writeTo(out, start, contentLength);
|
||||
}
|
||||
}
|
||||
|
||||
protected void putHeaders(HttpServletResponse response, HttpContent content, long contentLength)
|
||||
{
|
||||
if (response instanceof Response)
|
||||
|
|
|
@ -43,7 +43,7 @@ public class InputStreamRangeWriter implements RangeWriter
|
|||
private long pos;
|
||||
|
||||
/**
|
||||
* Create InputStremRangeWriter
|
||||
* Create InputStreamRangeWriter
|
||||
*
|
||||
* @param inputStreamSupplier Supplier of the InputStream. If the stream needs to be regenerated, such as the next
|
||||
* requested range being before the current position, then the current InputStream is closed and a new one obtained
|
||||
|
|
|
@ -594,94 +594,6 @@ public class PathResource extends Resource
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outputStream the output stream to write to
|
||||
* @param start First byte to write
|
||||
* @param count Bytes to write or -1 for all of them.
|
||||
* @throws IOException if unable to copy the Resource to the output
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream, long start, long count)
|
||||
throws IOException
|
||||
{
|
||||
long length = count;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
length = Files.size(path) - start;
|
||||
}
|
||||
|
||||
try (SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ))
|
||||
{
|
||||
ByteBuffer buffer = BufferUtil.allocate(IO.bufferSize);
|
||||
skipTo(channel, buffer, start);
|
||||
|
||||
// copy from channel to output stream
|
||||
long readTotal = 0;
|
||||
while (readTotal < length)
|
||||
{
|
||||
BufferUtil.clearToFill(buffer);
|
||||
int size = (int)Math.min(IO.bufferSize, length - readTotal);
|
||||
buffer.limit(size);
|
||||
int readLen = channel.read(buffer);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
BufferUtil.writeTo(buffer, outputStream);
|
||||
readTotal += readLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void skipTo(SeekableByteChannel channel, ByteBuffer buffer, long skipTo) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (channel.position() != skipTo)
|
||||
{
|
||||
channel.position(skipTo);
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
final int NO_PROGRESS_LIMIT = 3;
|
||||
|
||||
if (skipTo > 0)
|
||||
{
|
||||
long pos = 0;
|
||||
long readLen;
|
||||
int noProgressLoopLimit = NO_PROGRESS_LIMIT;
|
||||
// loop till we reach desired point, break out on lack of progress.
|
||||
while (noProgressLoopLimit > 0 && pos < skipTo)
|
||||
{
|
||||
BufferUtil.clearToFill(buffer);
|
||||
int len = (int)Math.min(IO.bufferSize, (skipTo - pos));
|
||||
buffer.limit(len);
|
||||
readLen = channel.read(buffer);
|
||||
if (readLen == 0)
|
||||
{
|
||||
noProgressLoopLimit--;
|
||||
}
|
||||
else if (readLen > 0)
|
||||
{
|
||||
pos += readLen;
|
||||
noProgressLoopLimit = NO_PROGRESS_LIMIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// negative values means the stream was closed or reached EOF
|
||||
// either way, we've hit a state where we can no longer
|
||||
// fulfill the requested range write.
|
||||
throw new IOException("EOF reached before SeekableByteChannel skip destination");
|
||||
}
|
||||
}
|
||||
|
||||
if (noProgressLoopLimit <= 0)
|
||||
{
|
||||
throw new IOException("No progress made to reach SeekableByteChannel skip position " + skipTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -28,7 +28,9 @@ import java.net.MalformedURLException;
|
|||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
|
@ -818,25 +820,6 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
return StringUtil.sanitizeXmlString(raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param out the output stream to write to
|
||||
* @param start First byte to write
|
||||
* @param count Bytes to write or -1 for all of them.
|
||||
* @throws IOException if unable to copy the Resource to the output
|
||||
*/
|
||||
public void writeTo(OutputStream out, long start, long count)
|
||||
throws IOException
|
||||
{
|
||||
try (InputStream in = getInputStream())
|
||||
{
|
||||
in.skip(start);
|
||||
if (count < 0)
|
||||
IO.copy(in, out);
|
||||
else
|
||||
IO.copy(in, out, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Resource to the new destination file.
|
||||
* <p>
|
||||
|
@ -851,9 +834,20 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
if (destination.exists())
|
||||
throw new IllegalArgumentException(destination + " exists");
|
||||
|
||||
try (OutputStream out = new FileOutputStream(destination))
|
||||
// attempt simple file copy
|
||||
File src = getFile();
|
||||
if (src != null)
|
||||
{
|
||||
writeTo(out, 0, -1);
|
||||
Files.copy(src.toPath(), destination.toPath(),
|
||||
StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
return;
|
||||
}
|
||||
|
||||
// use old school stream based copy
|
||||
try (InputStream in = getInputStream();
|
||||
OutputStream out = new FileOutputStream(destination))
|
||||
{
|
||||
IO.copy(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,32 +111,6 @@ public class PathResourceTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonDefaultFileSystemWriteTo() throws URISyntaxException, IOException
|
||||
{
|
||||
Path exampleJar = MavenTestingUtils.getTestResourcePathFile("example.jar");
|
||||
|
||||
URI uri = new URI("jar", exampleJar.toUri().toASCIIString(), null);
|
||||
|
||||
Map<String, Object> env = new HashMap<>();
|
||||
env.put("multi-release", "runtime");
|
||||
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(uri, env))
|
||||
{
|
||||
Path manifestPath = zipfs.getPath("/META-INF/MANIFEST.MF");
|
||||
assertThat(manifestPath, is(not(nullValue())));
|
||||
|
||||
PathResource resource = new PathResource(manifestPath);
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream())
|
||||
{
|
||||
resource.writeTo(out, 2, 10);
|
||||
String actual = new String(out.toByteArray(), UTF_8);
|
||||
String expected = "nifest-Ver";
|
||||
assertThat("writeTo(out, 2, 10)", actual, is(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultFileSystemGetFile() throws Exception
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue