This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 9135b45 org.apache.juneau.http tests.
9135b45 is described below
commit 9135b452833e6d7a2dd7254624231ff649f7d675
Author: JamesBognar <[email protected]>
AuthorDate: Wed Jul 8 09:49:41 2020 -0400
org.apache.juneau.http tests.
---
.../org/apache/juneau/http/ReaderResource.java | 18 --
.../apache/juneau/internal/ReaderInputStream.java | 280 +++++++++++++++++
.../apache/juneau/internal/WriterOutputStream.java | 340 +++++++++++++++++++++
.../org/apache/juneau/rest/client2/RestClient.java | 15 +-
.../apache/juneau/rest/client2/RestRequest.java | 12 +-
.../apache/juneau/rest/mock2/MockRestClient.java | 9 +-
.../apache/juneau/rest/BasicRestCallHandler.java | 12 +-
.../apache/juneau/rest/BasicRestCallLogger.java | 4 +
.../java/org/apache/juneau/rest/RestContext.java | 58 ++--
.../java/org/apache/juneau/rest/RestRequest.java | 30 +-
.../java/org/apache/juneau/rest/StaticFile.java | 85 ++++++
.../java/org/apache/juneau/rest/StaticFiles.java | 18 +-
.../java/org/apache/juneau/rest/StatusStats.java | 6 +-
.../juneau/rest/reshandlers/DefaultHandler.java | 24 +-
.../java/org/apache/juneau/rest/vars/FileVar.java | 15 +-
15 files changed, 835 insertions(+), 91 deletions(-)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
index 9b16e31..3aef2bb 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
@@ -140,24 +140,6 @@ public class ReaderResource implements Writable {
}
/**
- * Same as {@link #toString()} but strips comments from the text before
returning it.
- *
- * <p>
- * Supports stripping comments from the following media types: HTML,
XHTML, XML, JSON, Javascript, CSS.
- *
- * @return The resource contents stripped of any comments.
- */
- public String toCommentStrippedString() {
- String s = toString();
- String subType = mediaType.getSubType();
- if ("html".equals(subType) || "xhtml".equals(subType) ||
"xml".equals(subType))
- s = s.replaceAll("(?s)<!--(.*?)-->\\s*", "");
- else if ("json".equals(subType) || "javascript".equals(subType)
|| "css".equals(subType))
- s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
- return s;
- }
-
- /**
* Returns the contents of this resource.
*
* @return The contents of this resource.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReaderInputStream.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReaderInputStream.java
new file mode 100644
index 0000000..8f597b6
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReaderInputStream.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.util.Objects;
+
+/**
+ * {@link InputStream} implementation that reads a character stream from a
{@link Reader}
+ * and transforms it to a byte stream using a specified charset encoding. The
stream
+ * is transformed using a {@link CharsetEncoder} object, guaranteeing that all
charset
+ * encodings supported by the JRE are handled correctly. In particular for
charsets such as
+ * UTF-16, the implementation ensures that one and only one byte order marker
+ * is produced.
+ * <p>
+ * Since in general it is not possible to predict the number of characters to
be read from the
+ * {@link Reader} to satisfy a read request on the {@link ReaderInputStream},
all reads from
+ * the {@link Reader} are buffered. There is therefore no well defined
correlation
+ * between the current position of the {@link Reader} and that of the {@link
ReaderInputStream}.
+ * This also implies that in general there is no need to wrap the underlying
{@link Reader}
+ * in a {@link java.io.BufferedReader}.
+ * <p>
+ * {@link ReaderInputStream} implements the inverse transformation of {@link
java.io.InputStreamReader};
+ * in the following example, reading from {@code in2} would return the same
byte
+ * sequence as reading from {@code in} (provided that the initial byte
sequence is legal
+ * with respect to the charset encoding):
+ * <pre>
+ * InputStream in = ...
+ * Charset cs = ...
+ * InputStreamReader reader = new InputStreamReader(in, cs);
+ * ReaderInputStream in2 = new ReaderInputStream(reader, cs);</pre>
+ * {@link ReaderInputStream} implements the same transformation as {@link
java.io.OutputStreamWriter},
+ * except that the control flow is reversed: both classes transform a
character stream
+ * into a byte stream, but {@link java.io.OutputStreamWriter} pushes data to
the underlying stream,
+ * while {@link ReaderInputStream} pulls it from the underlying stream.
+ * <p>
+ * Note that while there are use cases where there is no alternative to using
+ * this class, very often the need to use this class is an indication of a flaw
+ * in the design of the code. This class is typically used in situations where
an existing
+ * API only accepts an {@link InputStream}, but where the most natural way to
produce the data
+ * is as a character stream, i.e. by providing a {@link Reader} instance. An
example of a situation
+ * where this problem may appear is when implementing the {@code
javax.activation.DataSource}
+ * interface from the Java Activation Framework.
+ * <p>
+ * Given the fact that the {@link Reader} class doesn't provide any way to
predict whether the next
+ * read operation will block or not, it is not possible to provide a meaningful
+ * implementation of the {@link InputStream#available()} method. A call to
this method
+ * will always return 0. Also, this class doesn't support {@link
InputStream#mark(int)}.
+ * <p>
+ * Instances of {@link ReaderInputStream} are not thread safe.
+ *
+ * @since 2.0
+ */
+public class ReaderInputStream extends InputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ private final Reader reader;
+ private final CharsetEncoder encoder;
+
+ /**
+ * CharBuffer used as input for the decoder. It should be reasonably
+ * large as we read data from the underlying Reader into this buffer.
+ */
+ private final CharBuffer encoderIn;
+
+ /**
+ * ByteBuffer used as output for the decoder. This buffer can be small
+ * as it is only used to transfer data from the decoder to the
+ * buffer provided by the caller.
+ */
+ private final ByteBuffer encoderOut;
+
+ private CoderResult lastCoderResult;
+ private boolean endOfInput;
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param encoder the charset encoder
+ * @since 2.1
+ */
+ public ReaderInputStream(final Reader reader, final CharsetEncoder
encoder) {
+ this(reader, encoder, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param encoder the charset encoder
+ * @param bufferSize the size of the input buffer in number of characters
+ * @since 2.1
+ */
+ public ReaderInputStream(final Reader reader, final CharsetEncoder
encoder, final int bufferSize) {
+ this.reader = reader;
+ this.encoder = encoder;
+ this.encoderIn = CharBuffer.allocate(bufferSize);
+ this.encoderIn.flip();
+ this.encoderOut = ByteBuffer.allocate(128);
+ this.encoderOut.flip();
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param charset the charset encoding
+ * @param bufferSize the size of the input buffer in number of characters
+ */
+ public ReaderInputStream(final Reader reader, final Charset charset, final
int bufferSize) {
+ this(reader,
+ charset.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE),
+ bufferSize);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream} with a default input buffer
size of
+ * {@value 1024} characters.
+ *
+ * @param reader the target {@link Reader}
+ * @param charset the charset encoding
+ */
+ public ReaderInputStream(final Reader reader, final Charset charset) {
+ this(reader, charset, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param charsetName the name of the charset encoding
+ * @param bufferSize the size of the input buffer in number of characters
+ */
+ public ReaderInputStream(final Reader reader, final String charsetName,
final int bufferSize) {
+ this(reader, Charset.forName(charsetName), bufferSize);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream} with a default input buffer
size of
+ * {@value 1024} characters.
+ *
+ * @param reader the target {@link Reader}
+ * @param charsetName the name of the charset encoding
+ */
+ public ReaderInputStream(final Reader reader, final String charsetName) {
+ this(reader, charsetName, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Fills the internal char buffer from the reader.
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ private void fillBuffer() throws IOException {
+ if (!endOfInput && (lastCoderResult == null ||
lastCoderResult.isUnderflow())) {
+ encoderIn.compact();
+ final int position = encoderIn.position();
+ // We don't use Reader#read(CharBuffer) here because it is more
efficient
+ // to write directly to the underlying char array (the default
implementation
+ // copies data to a temporary char array).
+ final int c = reader.read(encoderIn.array(), position,
encoderIn.remaining());
+ if (c == -1) {
+ endOfInput = true;
+ } else {
+ encoderIn.position(position+c);
+ }
+ encoderIn.flip();
+ }
+ encoderOut.compact();
+ lastCoderResult = encoder.encode(encoderIn, encoderOut, endOfInput);
+ encoderOut.flip();
+ }
+
+ /**
+ * Read the specified number of bytes into an array.
+ *
+ * @param array the byte array to read into
+ * @param off the offset to start reading bytes into
+ * @param len the number of bytes to read
+ * @return the number of bytes read or <code>-1</code>
+ * if the end of the stream has been reached
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public int read(final byte[] array, int off, int len) throws IOException {
+ Objects.requireNonNull(array, "array");
+ if (len < 0 || off < 0 || (off + len) > array.length) {
+ throw new IndexOutOfBoundsException("Array Size=" + array.length +
+ ", offset=" + off + ", length=" + len);
+ }
+ int read = 0;
+ if (len == 0) {
+ return 0; // Always return 0 if len == 0
+ }
+ while (len > 0) {
+ if (encoderOut.hasRemaining()) {
+ final int c = Math.min(encoderOut.remaining(), len);
+ encoderOut.get(array, off, c);
+ off += c;
+ len -= c;
+ read += c;
+ } else {
+ fillBuffer();
+ if (endOfInput && !encoderOut.hasRemaining()) {
+ break;
+ }
+ }
+ }
+ return read == 0 && endOfInput ? -1 : read;
+ }
+
+ /**
+ * Read the specified number of bytes into an array.
+ *
+ * @param b the byte array to read into
+ * @return the number of bytes read or <code>-1</code>
+ * if the end of the stream has been reached
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public int read(final byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Read a single byte.
+ *
+ * @return either the byte read or <code>-1</code> if the end of the stream
+ * has been reached
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public int read() throws IOException {
+ for (;;) {
+ if (encoderOut.hasRemaining()) {
+ return encoderOut.get() & 0xFF;
+ }
+ fillBuffer();
+ if (endOfInput && !encoderOut.hasRemaining()) {
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Close the stream. This method will cause the underlying {@link Reader}
+ * to be closed.
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/WriterOutputStream.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/WriterOutputStream.java
new file mode 100644
index 0000000..e14a4f6
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/WriterOutputStream.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.internal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * {@link OutputStream} implementation that transforms a byte stream to a
+ * character stream using a specified charset encoding and writes the resulting
+ * stream to a {@link Writer}. The stream is transformed using a
+ * {@link CharsetDecoder} object, guaranteeing that all charset
+ * encodings supported by the JRE are handled correctly.
+ * <p>
+ * The output of the {@link CharsetDecoder} is buffered using a fixed size
buffer.
+ * This implies that the data is written to the underlying {@link Writer} in
chunks
+ * that are no larger than the size of this buffer. By default, the buffer is
+ * flushed only when it overflows or when {@link #flush()} or {@link #close()}
+ * is called. In general there is therefore no need to wrap the underlying
{@link Writer}
+ * in a {@link java.io.BufferedWriter}. {@link WriterOutputStream} can also
+ * be instructed to flush the buffer after each write operation. In this case,
all
+ * available data is written immediately to the underlying {@link Writer},
implying that
+ * the current position of the {@link Writer} is correlated to the current
position
+ * of the {@link WriterOutputStream}.
+ * <p>
+ * {@link WriterOutputStream} implements the inverse transformation of {@link
java.io.OutputStreamWriter};
+ * in the following example, writing to {@code out2} would have the same
result as writing to
+ * {@code out} directly (provided that the byte sequence is legal with respect
to the
+ * charset encoding):
+ * <pre>
+ * OutputStream out = ...
+ * Charset cs = ...
+ * OutputStreamWriter writer = new OutputStreamWriter(out, cs);
+ * WriterOutputStream out2 = new WriterOutputStream(writer, cs);</pre>
+ * {@link WriterOutputStream} implements the same transformation as {@link
java.io.InputStreamReader},
+ * except that the control flow is reversed: both classes transform a byte
stream
+ * into a character stream, but {@link java.io.InputStreamReader} pulls data
from the underlying stream,
+ * while {@link WriterOutputStream} pushes it to the underlying stream.
+ * <p>
+ * Note that while there are use cases where there is no alternative to using
+ * this class, very often the need to use this class is an indication of a flaw
+ * in the design of the code. This class is typically used in situations where
an existing
+ * API only accepts an {@link OutputStream} object, but where the stream is
known to represent
+ * character data that must be decoded for further use.
+ * <p>
+ * Instances of {@link WriterOutputStream} are not thread safe.
+ *
+ * @since 2.0
+ */
+public class WriterOutputStream extends OutputStream {
+ private static final int BUFFER_SIZE = 1024;
+
+ private final Writer writer;
+ private final CharsetDecoder decoder;
+ private final boolean writeImmediately;
+
+ /**
+ * ByteBuffer used as input for the decoder. This buffer can be small
+ * as it is used only to transfer the received data to the
+ * decoder.
+ */
+ private final ByteBuffer decoderIn = ByteBuffer.allocate(128);
+
+ /**
+ * CharBuffer used as output for the decoder. It should be
+ * somewhat larger as we write from this buffer to the
+ * underlying Writer.
+ */
+ private final CharBuffer decoderOut;
+
+ /**
+ * Constructs a new {@link WriterOutputStream} with a default output
buffer size of 1024
+ * characters. The output buffer will only be flushed when it overflows or
when {@link #flush()} or {@link #close()}
+ * is called.
+ *
+ * @param writer the target {@link Writer}
+ * @param decoder the charset decoder
+ * @since 2.1
+ */
+ public WriterOutputStream(final Writer writer, final CharsetDecoder
decoder) {
+ this(writer, decoder, BUFFER_SIZE, false);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream}.
+ *
+ * @param writer the target {@link Writer}
+ * @param decoder the charset decoder
+ * @param bufferSize the size of the output buffer in number of characters
+ * @param writeImmediately If {@code true} the output buffer will be
flushed after each
+ * write operation, i.e. all available data will
be written to the
+ * underlying {@link Writer} immediately. If
{@code false}, the
+ * output buffer will only be flushed when it
overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ * @since 2.1
+ */
+ public WriterOutputStream(final Writer writer, final CharsetDecoder
decoder, final int bufferSize,
+ final boolean writeImmediately) {
+ checkIbmJdkWithBrokenUTF16( decoder.charset());
+ this.writer = writer;
+ this.decoder = decoder;
+ this.writeImmediately = writeImmediately;
+ decoderOut = CharBuffer.allocate(bufferSize);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream}.
+ *
+ * @param writer the target {@link Writer}
+ * @param charset the charset encoding
+ * @param bufferSize the size of the output buffer in number of characters
+ * @param writeImmediately If {@code true} the output buffer will be
flushed after each
+ * write operation, i.e. all available data will
be written to the
+ * underlying {@link Writer} immediately. If
{@code false}, the
+ * output buffer will only be flushed when it
overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ */
+ public WriterOutputStream(final Writer writer, final Charset charset,
final int bufferSize,
+ final boolean writeImmediately) {
+ this(writer,
+ charset.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ .replaceWith("?"),
+ bufferSize,
+ writeImmediately);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream} with a default output
buffer size of 1024
+ * characters. The output buffer will only be flushed when it overflows or
when {@link #flush()} or {@link #close()}
+ * is called.
+ *
+ * @param writer the target {@link Writer}
+ * @param charset the charset encoding
+ */
+ public WriterOutputStream(final Writer writer, final Charset charset) {
+ this(writer, charset, BUFFER_SIZE, false);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream}.
+ *
+ * @param writer the target {@link Writer}
+ * @param charsetName the name of the charset encoding
+ * @param bufferSize the size of the output buffer in number of characters
+ * @param writeImmediately If {@code true} the output buffer will be
flushed after each
+ * write operation, i.e. all available data will
be written to the
+ * underlying {@link Writer} immediately. If
{@code false}, the
+ * output buffer will only be flushed when it
overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ */
+ public WriterOutputStream(final Writer writer, final String charsetName,
final int bufferSize,
+ final boolean writeImmediately) {
+ this(writer, Charset.forName(charsetName), bufferSize,
writeImmediately);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream} with a default output
buffer size of 1024
+ * characters. The output buffer will only be flushed when it overflows or
when {@link #flush()} or {@link #close()}
+ * is called.
+ *
+ * @param writer the target {@link Writer}
+ * @param charsetName the name of the charset encoding
+ */
+ public WriterOutputStream(final Writer writer, final String charsetName) {
+ this(writer, charsetName, BUFFER_SIZE, false);
+ }
+
+ /**
+ * Write bytes from the specified byte array to the stream.
+ *
+ * @param b the byte array containing the bytes to write
+ * @param off the start offset in the byte array
+ * @param len the number of bytes to write
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void write(final byte[] b, int off, int len) throws IOException {
+ while (len > 0) {
+ final int c = Math.min(len, decoderIn.remaining());
+ decoderIn.put(b, off, c);
+ processInput(false);
+ len -= c;
+ off += c;
+ }
+ if (writeImmediately) {
+ flushOutput();
+ }
+ }
+
+ /**
+ * Write bytes from the specified byte array to the stream.
+ *
+ * @param b the byte array containing the bytes to write
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void write(final byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Write a single byte to the stream.
+ *
+ * @param b the byte to write
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void write(final int b) throws IOException {
+ write(new byte[] { (byte)b }, 0, 1);
+ }
+
+ /**
+ * Flush the stream. Any remaining content accumulated in the output buffer
+ * will be written to the underlying {@link Writer}. After that
+ * {@link Writer#flush()} will be called.
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void flush() throws IOException {
+ flushOutput();
+ writer.flush();
+ }
+
+ /**
+ * Close the stream. Any remaining content accumulated in the output buffer
+ * will be written to the underlying {@link Writer}. After that
+ * {@link Writer#close()} will be called.
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public void close() throws IOException {
+ processInput(true);
+ flushOutput();
+ writer.close();
+ }
+
+ /**
+ * Decode the contents of the input ByteBuffer into a CharBuffer.
+ *
+ * @param endOfInput indicates end of input
+ * @throws IOException if an I/O error occurs
+ */
+ private void processInput(final boolean endOfInput) throws IOException {
+ // Prepare decoderIn for reading
+ decoderIn.flip();
+ CoderResult coderResult;
+ while (true) {
+ coderResult = decoder.decode(decoderIn, decoderOut, endOfInput);
+ if (coderResult.isOverflow()) {
+ flushOutput();
+ } else if (coderResult.isUnderflow()) {
+ break;
+ } else {
+ // The decoder is configured to replace malformed input and
unmappable characters,
+ // so we should not get here.
+ throw new IOException("Unexpected coder result");
+ }
+ }
+ // Discard the bytes that have been read
+ decoderIn.compact();
+ }
+
+ /**
+ * Flush the output.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ private void flushOutput() throws IOException {
+ if (decoderOut.position() > 0) {
+ writer.write(decoderOut.array(), 0, decoderOut.position());
+ decoderOut.rewind();
+ }
+ }
+
+ /**
+ * Check if the JDK in use properly supports the given charset.
+ *
+ * @param charset the charset to check the support for
+ */
+ private static void checkIbmJdkWithBrokenUTF16(final Charset charset){
+ if (!"UTF-16".equals(charset.name())) {
+ return;
+ }
+ final String TEST_STRING_2 = "v\u00e9s";
+ final byte[] bytes = TEST_STRING_2.getBytes(charset);
+
+ final CharsetDecoder charsetDecoder2 = charset.newDecoder();
+ final ByteBuffer bb2 = ByteBuffer.allocate(16);
+ final CharBuffer cb2 = CharBuffer.allocate(TEST_STRING_2.length());
+ final int len = bytes.length;
+ for (int i = 0; i < len; i++) {
+ bb2.put(bytes[i]);
+ bb2.flip();
+ try {
+ charsetDecoder2.decode(bb2, cb2, i == (len - 1));
+ } catch ( final IllegalArgumentException e){
+ throw new UnsupportedOperationException("UTF-16 requested when
runninng on an IBM JDK with broken UTF-16 support. " +
+ "Please find a JDK that supports UTF-16 if you intend
to use UF-16 with WriterOutputStream");
+ }
+ bb2.compact();
+ }
+ cb2.rewind();
+ if (!TEST_STRING_2.equals(cb2.toString())){
+ throw new UnsupportedOperationException("UTF-16 requested when
runninng on an IBM JDK with broken UTF-16 support. " +
+ "Please find a JDK that supports UTF-16 if you intend to
use UF-16 with WriterOutputStream");
+ }
+
+ }
+}
+
+
+
+
+
+
+
+
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
index 4ba6eff..a97f0eb 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
@@ -486,9 +486,9 @@ import org.apache.juneau.utils.*;
* <li class='jc'>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li class='jc'>
- * {@link ReaderResource}/{@link ReaderResourceBuilder} -
Raw contents of {@code Reader} will be serialized to remote resource.
Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code Reader}
will be serialized to remote resource. Additional headers and media type will
be set on request.
* <li class='jc'>
- * {@link StreamResource}/{@link StreamResourceBuilder} -
Raw contents of {@code InputStream} will be serialized to remote resource.
Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li class='jc'>
* {@link HttpEntity} - Bypass Juneau serialization and
pass HttpEntity directly to HttpClient.
* <li class='jc'>
@@ -2504,7 +2504,8 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
* <li>{@link NameValuePair} array - URL-encoded as name
value pairs.
* <li>{@link NameValuePairSupplier} - URL-encoded as name
value pairs.
* <li>{@link Reader}/{@link InputStream}- Streamed
directly and <l>Content-Type</l> set to
<js>"application/x-www-form-urlencoded"</js>
- * <li>{@link ReaderResource}/{@link
ReaderResourceBuilder}/{@link StreamResource}/{@link
StreamResourceBuilder}/{@link HttpEntity}- Streamed directly and
<l>Content-Type</l> set to <js>"application/x-www-form-urlencoded"</js> if not
already specified on the entity.
+ * <li>{@link ReaderResource}/{@link StreamResource}-
Streamed directly and <l>Content-Type</l> set to
<js>"application/x-www-form-urlencoded"</js> if not already specified on the
entity.
+ * <li>{@link HttpEntity}- Streamed directly and
<l>Content-Type</l> set to <js>"application/x-www-form-urlencoded"</js> if not
already specified on the entity.
* <li>{@link Object} - Converted to a {@link
SerializedHttpEntity} using {@link UrlEncodingSerializer} to serialize.
* <li>{@link Supplier} - A supplier of anything on this
list.
* </ul>
@@ -2625,9 +2626,9 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
* <li>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li>
- * {@link ReaderResource}/{@link
ReaderResourceBuilder} - Raw contents of {@code Reader} will be serialized to
remote resource. Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code
Reader} will be serialized to remote resource. Additional headers and media
type will be set on request.
* <li>
- * {@link StreamResource}/{@link
StreamResourceBuilder} - Raw contents of {@code InputStream} will be serialized
to remote resource. Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li>
* {@link Object} - POJO to be converted to text
using the {@link Serializer} registered with the
* {@link RestClient}.
@@ -2811,9 +2812,9 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
* <li>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li>
- * {@link ReaderResource}/{@link
ReaderResourceBuilder} - Raw contents of {@code Reader} will be serialized to
remote resource. Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code
Reader} will be serialized to remote resource. Additional headers and media
type will be set on request.
* <li>
- * {@link StreamResource}/{@link
StreamResourceBuilder} - Raw contents of {@code InputStream} will be serialized
to remote resource. Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li>
* {@link Object} - POJO to be converted to text
using the {@link Serializer} registered with the
* {@link RestClient}.
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
index 8efc233..58a5ab6 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
@@ -1761,9 +1761,9 @@ public class RestRequest extends BeanSession implements
HttpUriRequest, Configur
* <li>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li>
- * {@link ReaderResource}/{@link
ReaderResourceBuilder} - Raw contents of {@code Reader} will be serialized to
remote resource. Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code
Reader} will be serialized to remote resource. Additional headers and media
type will be set on request.
* <li>
- * {@link StreamResource}/{@link
StreamResourceBuilder} - Raw contents of {@code InputStream} will be serialized
to remote resource. Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li>
* {@link Object} - POJO to be converted to text
using the {@link Serializer} registered with the
* {@link RestClient}.
@@ -1853,9 +1853,9 @@ public class RestRequest extends BeanSession implements
HttpUriRequest, Configur
* <li>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li>
- * {@link ReaderResource}/{@link
ReaderResourceBuilder} - Raw contents of {@code Reader} will be serialized to
remote resource. Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code
Reader} will be serialized to remote resource. Additional headers and media
type will be set on request.
* <li>
- * {@link StreamResource}/{@link
StreamResourceBuilder} - Raw contents of {@code InputStream} will be serialized
to remote resource. Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li>
* {@link Object} - POJO to be converted to text
using the {@link Serializer} registered with the
* {@link RestClient}.
@@ -1923,9 +1923,9 @@ public class RestRequest extends BeanSession implements
HttpUriRequest, Configur
* <li>
* {@link InputStream} - Raw contents of {@code
InputStream} will be serialized to remote resource.
* <li>
- * {@link ReaderResource}/{@link
ReaderResourceBuilder} - Raw contents of {@code Reader} will be serialized to
remote resource. Additional headers and media type will be set on request.
+ * {@link ReaderResource} - Raw contents of {@code
Reader} will be serialized to remote resource. Additional headers and media
type will be set on request.
* <li>
- * {@link StreamResource}/{@link
StreamResourceBuilder} - Raw contents of {@code InputStream} will be serialized
to remote resource. Additional headers and media type will be set on request.
+ * {@link StreamResource} - Raw contents of {@code
InputStream} will be serialized to remote resource. Additional headers and
media type will be set on request.
* <li>
* {@link Object} - POJO to be converted to text
using the {@link Serializer} registered with the
* {@link RestClient}.
diff --git
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestClient.java
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestClient.java
index 299c547..2f5fb3b 100644
---
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestClient.java
+++
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestClient.java
@@ -19,6 +19,7 @@ import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
+import java.util.zip.*;
import javax.servlet.http.*;
@@ -769,9 +770,11 @@ public class MockRestClient extends RestClient implements
HttpClientConnection {
@Override /* HttpClientConnection */
public void receiveResponseEntity(HttpResponse response) throws
HttpException, IOException {
- BasicHttpEntity e = new BasicHttpEntity();
- e.setContent(new ByteArrayInputStream(sres.get().getBody()));
- response.setEntity(e);
+ InputStream is = new ByteArrayInputStream(sres.get().getBody());
+ Header contentEncoding =
response.getLastHeader("Content-Encoding");
+ if (contentEncoding != null &&
contentEncoding.getValue().equalsIgnoreCase("gzip"))
+ is = new GZIPInputStream(is);
+ response.setEntity(new InputStreamEntity(is));
}
@Override /* HttpClientConnection */
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 262e868..6888649 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -25,9 +25,7 @@ import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.juneau.*;
-import org.apache.juneau.http.StreamResource;
import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.rest.RestContext.*;
import org.apache.juneau.http.exception.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.reflect.*;
@@ -147,13 +145,15 @@ public class BasicRestCallHandler implements
RestCallHandler {
context.setRequest(call.getRestRequest());
context.setResponse(call.getRestResponse());
- StreamResource r = null;
+ StaticFile r = null;
if (call.getPathInfoUndecoded() != null) {
String p =
call.getPathInfoUndecoded().substring(1);
if (context.isStaticFile(p)) {
- StaticFile sf =
context.resolveStaticFile(p);
- r = sf.resource;
- call.responseMeta(sf.meta);
+ r = context.resolveStaticFile(p);
+ if (! r.exists()) {
+ call.output(null);
+ r = null;
+ }
} else if (p.equals("favicon.ico")) {
call.output(null);
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
index 483e6ae..6002781 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
@@ -248,12 +248,16 @@ public class BasicRestCallLogger implements
RestCallLogger {
}
private byte[] getRequestBody(HttpServletRequest req) {
+ if (req instanceof RestRequest)
+ req = ((RestRequest)req).getInner();
if (req instanceof CachingHttpServletRequest)
return ((CachingHttpServletRequest)req).getBody();
return castOrNull(req.getAttribute("RequestBody"),
byte[].class);
}
private byte[] getResponseBody(HttpServletRequest req,
HttpServletResponse res) {
+ if (res instanceof RestResponse)
+ res = ((RestResponse)res).getInner();
if (res instanceof CachingHttpServletResponse)
return ((CachingHttpServletResponse)res).getBody();
return castOrNull(req.getAttribute("ResponseBody"),
byte[].class);
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 75c352f..770d66c 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -17,6 +17,7 @@ import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.IOUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.rest.util.RestUtils.*;
+import static org.apache.juneau.rest.Enablement.*;
import static org.apache.juneau.rest.HttpRuntimeException.*;
import static org.apache.juneau.BasicIllegalArgumentException.*;
@@ -41,7 +42,6 @@ import org.apache.juneau.encoders.*;
import org.apache.juneau.html.*;
import org.apache.juneau.html.annotation.*;
import org.apache.juneau.http.*;
-import org.apache.juneau.http.StreamResource;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.http.annotation.Body;
import org.apache.juneau.http.annotation.FormData;
@@ -3628,6 +3628,7 @@ public final class RestContext extends BeanContext {
private final UriRelativity uriRelativity;
private final ConcurrentHashMap<String,MethodExecStats> methodExecStats
= new ConcurrentHashMap<>();
private final Instant startTime;
+ private final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new
ConcurrentHashMap<>();
// Lifecycle methods
private final MethodInvoker[]
@@ -3794,7 +3795,9 @@ public final class RestContext extends BeanContext {
logger = getInstanceProperty(REST_logger, resource,
RestLogger.class, NoOpRestLogger.class, resourceResolver, this);
Object clc = getProperty(REST_callLoggerConfig);
- if (clc instanceof RestCallLoggerConfig)
+ if (this.debug == TRUE)
+ this.callLoggerConfig =
RestCallLoggerConfig.DEFAULT_DEBUG;
+ else if (clc instanceof RestCallLoggerConfig)
this.callLoggerConfig =
(RestCallLoggerConfig)clc;
else if (clc instanceof OMap)
this.callLoggerConfig =
RestCallLoggerConfig.create().apply((OMap)clc).build();
@@ -4269,13 +4272,14 @@ public final class RestContext extends BeanContext {
String p = urlDecode(trimSlashes(pathInfo));
if (p.indexOf("..") != -1)
throw new NotFound("Invalid path");
- StreamResource sr = null;
- for (StaticFiles sf : staticFiles) {
- sr = sf.resolve(p);
- if (sr != null)
+ StaticFile sf = null;
+ for (StaticFiles sfs : staticFiles) {
+ sf = sfs.resolve(p);
+ if (sf != null)
break;
}
- StaticFile sf = new StaticFile(sr);
+ if (sf == null)
+ sf = new StaticFile(null,null,null);
if (useClasspathResourceCaching) {
if (staticFilesCache.size() > 100)
staticFilesCache.clear();
@@ -4287,24 +4291,6 @@ public final class RestContext extends BeanContext {
}
/**
- * A cached static file instance.
- */
- class StaticFile {
- StreamResource resource;
- ResponseBeanMeta meta;
-
- /**
- * Constructor.
- *
- * @param resource The inner resource.
- */
- StaticFile(StreamResource resource) {
- this.resource = resource;
- this.meta = resource == null ? null :
ResponseBeanMeta.create(resource.getClass(), getPropertyStore());
- }
- }
-
- /**
* Same as {@link Class#getResourceAsStream(String)} except if it
doesn't find the resource on this class, searches
* up the parent hierarchy chain.
*
@@ -5433,6 +5419,28 @@ public final class RestContext extends BeanContext {
this.res.set(res);
}
+ /**
+ * If the specified object is annotated with {@link Response}, this
returns the response metadata about that object.
+ *
+ * @param o The object to check.
+ * @return The response metadata, or <jk>null</jk> if it wasn't
annotated with {@link Response}.
+ */
+ public ResponseBeanMeta getResponseBeanMeta(Object o) {
+ if (o == null)
+ return null;
+ Class<?> c = o.getClass();
+ ResponseBeanMeta rbm = responseBeanMetas.get(c);
+ if (rbm == null) {
+ rbm = ResponseBeanMeta.create(c,
serializers.getPropertyStore());
+ if (rbm == null)
+ rbm = ResponseBeanMeta.NULL;
+ responseBeanMetas.put(c, rbm);
+ }
+ if (rbm == ResponseBeanMeta.NULL)
+ return null;
+ return rbm;
+ }
+
Enablement getDebug() {
return debug;
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 6d71272..1f09414 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -1304,6 +1304,8 @@ public final class RestRequest extends
HttpServletRequestWrapper {
* @return The request bean session.
*/
public BeanSession getBeanSession() {
+ if (beanSession == null)
+ beanSession = context.createBeanSession();
return beanSession;
}
@@ -1468,6 +1470,32 @@ public final class RestRequest extends
HttpServletRequestWrapper {
}
/**
+ * Returns a classpath resource as a string,
+ *
+ * @param name The resource name.
+ * @return The resource contents, or <jk>null</jk> if they could not be
found.
+ * @throws IOException If a problem occurred reading the resource.
+ */
+ public String getClasspathResourceAsString(String name) throws
IOException {
+ return getClasspathResourceAsString(name, false);
+ }
+
+ /**
+ * Returns a classpath resource as a string,
+ *
+ * @param name The resource name.
+ * @param resolveVars Resolve any SVL variables in the string.
+ * @return The resource contents, or <jk>null</jk> if they could not be
found.
+ * @throws IOException If a problem occurred reading the resource.
+ */
+ public String getClasspathResourceAsString(String name, boolean
resolveVars) throws IOException {
+ String s = context.getClasspathResourceAsString(name,
getLocale());
+ if (resolveVars)
+ return varSession.resolve(s);
+ return s;
+ }
+
+ /**
* Same as {@link #getClasspathReaderResource(String, boolean,
MediaType, boolean)} except uses the resource mime-type map
* constructed using {@link RestContextBuilder#mimeTypes(String...)} to
determine the media type.
*
@@ -1816,7 +1844,7 @@ public final class RestRequest extends
HttpServletRequestWrapper {
* @return Metadata about the specified response object, or
<jk>null</jk> if it's not annotated with {@link Response @Response}.
*/
public ResponseBeanMeta getResponseBeanMeta(Object o) {
- return restJavaMethod == null ? null :
restJavaMethod.getResponseBeanMeta(o);
+ return restJavaMethod == null ?
this.context.getResponseBeanMeta(o) : restJavaMethod.getResponseBeanMeta(o);
}
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFile.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFile.java
new file mode 100644
index 0000000..bd1032e
--- /dev/null
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFile.java
@@ -0,0 +1,85 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
+// * with the License. You may obtain a copy of the License at
*
+// *
*
+// * http://www.apache.org/licenses/LICENSE-2.0
*
+// *
*
+// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
+// * specific language governing permissions and limitations under the
License. *
+//
***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.juneau.http.annotation.*;
+
+/**
+ * Instance of a static file sent as an HTTP response.
+ */
+@Response
+public class StaticFile {
+
+ private final byte[] contents;
+ private final String mediaType;
+ private final Map<String,Object> headers;
+
+ /**
+ * Constructor.
+ *
+ * @param contents Contents of the file, or <jk>null</jk> if file does
not exist.
+ * @param mediaType The media type of the file.
+ * @param headers Arbitrary response headers to set when sending this
file as an HTTP response.
+ */
+ public StaticFile(byte[] contents, String mediaType, Map<String,Object>
headers) {
+ this.contents = contents;
+ this.mediaType = mediaType;
+ this.headers = headers;
+ }
+
+ /**
+ * Does this file exist?
+ *
+ * @return <jk>true</jk> if this file exists.
+ */
+ public boolean exists() {
+ return contents != null;
+ }
+
+ /**
+ * Get the HTTP response headers.
+ *
+ * @return
+ * The HTTP response headers.
+ * <br>An unmodifiable map.
+ * <br>Never <jk>null</jk>.
+ */
+ @ResponseHeader("*")
+ public Map<String,Object> getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Returns the contents of this static file as an input stream.
+ *
+ * @return This file as an input stream.
+ * @throws IOException Should never happen.
+ */
+ @ResponseBody
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(contents);
+ }
+
+ /**
+ * Returns the content type for this static file.
+ *
+ * @return The content type for this static file.
+ */
+ @ResponseHeader("Content-Type")
+ public String getContentType() {
+ return mediaType == null ? null : mediaType.toString();
+ }
+}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
index e7d859d..35aa3c0 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
@@ -10,18 +10,6 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
// * specific language governing permissions and limitations under the
License. *
//
***************************************************************************************************************************
-//
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
-// * with the License. You may obtain a copy of the License at
*
-// *
*
-// * http://www.apache.org/licenses/LICENSE-2.0
*
-// *
*
-// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
-// * specific language governing permissions and limitations under the
License. *
-//
***************************************************************************************************************************
package org.apache.juneau.rest;
import java.io.*;
@@ -29,7 +17,7 @@ import java.util.*;
import javax.activation.*;
-import org.apache.juneau.http.*;
+import org.apache.juneau.internal.*;
import org.apache.juneau.utils.*;
/**
@@ -56,7 +44,7 @@ class StaticFiles {
return path;
}
- StreamResource resolve(String p) throws IOException {
+ StaticFile resolve(String p) throws IOException {
if (p.startsWith(path)) {
String remainder = (p.equals(path) ? "" :
p.substring(path.length()));
if (remainder.isEmpty() || remainder.startsWith("/")) {
@@ -66,7 +54,7 @@ class StaticFiles {
int i = p2.lastIndexOf('/');
String name = (i == -1 ? p2 :
p2.substring(i+1));
String mediaType =
mimetypesFileTypeMap.getContentType(name);
- return new
StreamResource(MediaType.forString(mediaType), responseHeaders, true, is);
+ return new
StaticFile(IOUtils.readBytes(is), mediaType, responseHeaders);
}
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
index 58e14c8..d42050a 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
@@ -57,7 +57,7 @@ public class StatusStats implements Comparable<StatusStats> {
private java.lang.reflect.Method method;
private Set<Status> codes = new TreeSet<>();
- private Method(java.lang.reflect.Method method) {
+ Method(java.lang.reflect.Method method) {
this.method = method;
}
@@ -86,6 +86,10 @@ public class StatusStats implements Comparable<StatusStats> {
public int compareTo(Status o) {
return Integer.compare(code, o.code);
}
+
+ public int getCount() {
+ return count;
+ }
}
@Override
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index 7f51b6f..73ea7a5 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -28,6 +28,7 @@ import org.apache.juneau.http.exception.*;
import org.apache.juneau.rest.util.FinishablePrintWriter;
import org.apache.juneau.rest.util.FinishableServletOutputStream;
import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
/**
* Response handler for POJOs not handled by other handlers.
@@ -191,11 +192,24 @@ public class DefaultHandler implements ResponseHandler {
String out = null;
if (isEmpty(res.getContentType()))
res.setContentType("text/plain");
- out =
req.getBeanSession().getClassMetaForObject(o).toString(o);
- FinishablePrintWriter w = res.getNegotiatedWriter();
- w.append(out);
- w.flush();
- w.finish();
+ if (o instanceof InputStream) {
+ try (OutputStream os =
res.getNegotiatedOutputStream()) {
+ IOPipe.create(o, os).run();
+ os.flush();
+ }
+ } else if (o instanceof Reader) {
+ try (FinishablePrintWriter w =
res.getNegotiatedWriter()) {
+ IOPipe.create(o, w).run();
+ w.flush();
+ w.finish();
+ }
+ } else {
+ out =
req.getBeanSession().getClassMetaForObject(o).toString(o);
+ FinishablePrintWriter w =
res.getNegotiatedWriter();
+ w.append(out);
+ w.flush();
+ w.finish();
+ }
return true;
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
index 8623a64..cc3de68 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
@@ -12,7 +12,7 @@
//
***************************************************************************************************************************
package org.apache.juneau.rest.vars;
-import org.apache.juneau.http.ReaderResource;
+import org.apache.juneau.internal.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.svl.*;
import org.apache.juneau.utils.*;
@@ -25,7 +25,7 @@ import org.apache.juneau.utils.*;
*
* <p>
* File variables resolve to the contents of resource files located on the
classpath or local JVM directory.
- * They use the {@link RestRequest#getClasspathReaderResource(String)} method
to retrieve the contents of the file.
+ * They use the {@link RestRequest#getClasspathResourceAsString(String)}
method to retrieve the contents of the file.
* That in turn uses the {@link ClasspathResourceFinder} associated with the
servlet class to find the file.
*
* <p>
@@ -84,8 +84,15 @@ public class FileVar extends DefaultingVar {
RestRequest req = session.getSessionObject(RestRequest.class,
SESSION_req, false);
if (req != null) {
- ReaderResource rr = req.getClasspathReaderResource(key);
- return (rr == null ? null :
rr.toCommentStrippedString());
+ String s = req.getClasspathResourceAsString(key);
+ if (s == null)
+ return null;
+ String subType = FileUtils.getExtension(key);
+ if ("html".equals(subType) || "xhtml".equals(subType)
|| "xml".equals(subType))
+ s = s.replaceAll("(?s)<!--(.*?)-->\\s*", "");
+ else if ("json".equals(subType) ||
"javascript".equals(subType) || "css".equals(subType))
+ s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*",
"");
+ return s;
}
ClasspathResourceManager crm =
session.getSessionObject(ClasspathResourceManager.class, SESSION_crm, false);