/
HttpServletResponseOutputWrapper.java
183 lines (152 loc) · 5.82 KB
/
HttpServletResponseOutputWrapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* Copyright 2018 OmniFaces
*
* Licensed 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.omnifaces.servlet;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.omnifaces.io.DefaultServletOutputStream;
import org.omnifaces.io.ResettableBuffer;
import org.omnifaces.io.ResettableBufferedOutputStream;
import org.omnifaces.io.ResettableBufferedWriter;
/**
* Convenience class for extending {@link HttpServletResponseWrapper} wherein the servlet response {@link OutputStream}
* has to be replaced by a custom implementation. This saves the developer from writing repeated
* {@link #getOutputStream()}, {@link #getWriter()} and {@link #flushBuffer()} boilerplate. All the developer has to do
* is to implement the {@link #createOutputStream()} accordingly. This will in turn be used by both
* {@link #getOutputStream()} and {@link #getWriter()}.
* <p>
* The boolean property <code>passThrough</code>, which defaults to <code>false</code> also enables the developer to
* control whether to pass through to the wrapped {@link ServletOutputStream} or not.
* <p>
*
* @author Bauke Scholtz
* @since 1.1
*/
public abstract class HttpServletResponseOutputWrapper extends HttpServletResponseWrapper {
// Constants ------------------------------------------------------------------------------------------------------
private static final String ERROR_GETOUTPUT_ALREADY_CALLED =
"getOutputStream() has already been called on this response.";
private static final String ERROR_GETWRITER_ALREADY_CALLED =
"getWriter() has already been called on this response.";
// Properties -----------------------------------------------------------------------------------------------------
private ServletOutputStream output;
private PrintWriter writer;
private ResettableBuffer buffer;
private boolean passThrough;
// Constructors ---------------------------------------------------------------------------------------------------
/**
* Construct a new {@link HttpServletResponseOutputWrapper} which wraps the given response.
* @param wrappedResponse The wrapped response.
*/
public HttpServletResponseOutputWrapper(HttpServletResponse wrappedResponse) {
super(wrappedResponse);
}
// Actions --------------------------------------------------------------------------------------------------------
/**
* Returns the custom implementation of the servlet response {@link OutputStream}.
* @return The custom implementation of the servlet response {@link OutputStream}.
*/
protected abstract OutputStream createOutputStream();
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (passThrough) {
return super.getOutputStream();
}
if (writer != null) {
throw new IllegalStateException(ERROR_GETWRITER_ALREADY_CALLED);
}
if (output == null) {
buffer = new ResettableBufferedOutputStream(createOutputStream(), getBufferSize());
output = new DefaultServletOutputStream((OutputStream) buffer);
}
return output;
}
@Override
public PrintWriter getWriter() throws IOException {
if (passThrough) {
return super.getWriter();
}
if (output != null) {
throw new IllegalStateException(ERROR_GETOUTPUT_ALREADY_CALLED);
}
if (writer == null) {
buffer = new ResettableBufferedWriter(new OutputStreamWriter(createOutputStream(),
getCharacterEncoding()), getBufferSize(), getCharacterEncoding());
writer = new PrintWriter((Writer) buffer);
}
return writer;
}
@Override
public void flushBuffer() throws IOException {
super.flushBuffer();
if (passThrough) {
return;
}
if (writer != null) {
writer.flush();
}
else if (output != null) {
output.flush();
}
}
/**
* Close the response body. This closes any created writer or output stream.
* @throws IOException When an I/O error occurs.
*/
public void close() throws IOException {
if (writer != null) {
writer.close();
}
else if (output != null) {
output.close();
}
}
@Override
public void reset() {
super.reset();
if (buffer != null) {
buffer.reset();
}
}
// Getters/setters ------------------------------------------------------------------------------------------------
/**
* Returns whether the response is committed or not. The response is also considered committed when the resettable
* buffer has been flushed.
* @return <code>true</code> if the response is committed, otherwise <code>false</code>.
*/
@Override
public boolean isCommitted() {
return super.isCommitted() || (buffer != null && !buffer.isResettable());
}
/**
* Returns whether the writing has to be passed through to the wrapped {@link ServletOutputStream}.
* @return <code>true</code>, if the writing has to be passed through to the wrapped {@link ServletOutputStream},
* otherwise <code>false</code>.
*/
public boolean isPassThrough() {
return passThrough;
}
/**
* Sets whether the writing has to be passed through to the wrapped {@link ServletOutputStream}.
* @param passThrough set to <code>true</code> if the writing has to be passed through to the wrapped
* {@link ServletOutputStream}.
*/
public void setPassThrough(boolean passThrough) {
this.passThrough = passThrough;
}
}