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.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -46,9 +47,11 @@ import org.eclipse.jetty.http.QuotedCSV;
|
||||||
import org.eclipse.jetty.http.QuotedQualityCSV;
|
import org.eclipse.jetty.http.QuotedQualityCSV;
|
||||||
import org.eclipse.jetty.io.WriterOutputStream;
|
import org.eclipse.jetty.io.WriterOutputStream;
|
||||||
import org.eclipse.jetty.server.resource.HttpContentRangeWriter;
|
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.server.resource.RangeWriter;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.MultiPartOutputStream;
|
import org.eclipse.jetty.util.MultiPartOutputStream;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -669,18 +672,14 @@ public class ResourceService
|
||||||
if (include)
|
if (include)
|
||||||
{
|
{
|
||||||
// write without headers
|
// 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 we can't do a bypass write because of wrapping
|
||||||
else if (written || !(out instanceof HttpOutput))
|
else if (written || !(out instanceof HttpOutput))
|
||||||
{
|
{
|
||||||
// write normally
|
// write normally
|
||||||
putHeaders(response, content, written ? -1 : 0);
|
putHeaders(response, content, written ? -1 : 0);
|
||||||
ByteBuffer buffer = content.getIndirectBuffer();
|
writeContent(content, out, 0, content_length);
|
||||||
if (buffer != null)
|
|
||||||
BufferUtil.writeTo(buffer, out);
|
|
||||||
else
|
|
||||||
content.getResource().writeTo(out, 0, content_length);
|
|
||||||
}
|
}
|
||||||
// else do a bypass write
|
// else do a bypass write
|
||||||
else
|
else
|
||||||
|
@ -753,7 +752,7 @@ public class ResourceService
|
||||||
response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
|
response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
|
||||||
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
||||||
singleSatisfiableRange.toHeaderRangeString(content_length));
|
singleSatisfiableRange.toHeaderRangeString(content_length));
|
||||||
content.getResource().writeTo(out, singleSatisfiableRange.getFirst(), singleLength);
|
writeContent(content, out, singleSatisfiableRange.getFirst(), singleLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,6 +814,33 @@ public class ResourceService
|
||||||
return true;
|
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)
|
protected void putHeaders(HttpServletResponse response, HttpContent content, long contentLength)
|
||||||
{
|
{
|
||||||
if (response instanceof Response)
|
if (response instanceof Response)
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class InputStreamRangeWriter implements RangeWriter
|
||||||
private long pos;
|
private long pos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create InputStremRangeWriter
|
* Create InputStreamRangeWriter
|
||||||
*
|
*
|
||||||
* @param inputStreamSupplier Supplier of the InputStream. If the stream needs to be regenerated, such as the next
|
* @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
|
* 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
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,9 @@ import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
@ -818,25 +820,6 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
return StringUtil.sanitizeXmlString(raw);
|
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.
|
* Copy the Resource to the new destination file.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -851,9 +834,20 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
if (destination.exists())
|
if (destination.exists())
|
||||||
throw new IllegalArgumentException(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
|
@Test
|
||||||
public void testDefaultFileSystemGetFile() throws Exception
|
public void testDefaultFileSystemGetFile() throws Exception
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue