Skip to content

Commit e90402b

Browse files
committed
EntityInputStream#isEmpty refactoring
Signed-off-by: Maxim Nesen <[email protected]>
1 parent 318e355 commit e90402b

File tree

7 files changed

+238
-15
lines changed

7 files changed

+238
-15
lines changed

Diff for: containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.glassfish.jersey.servlet;
1818

1919
import java.io.IOException;
20-
import java.io.UncheckedIOException;
2120
import java.lang.reflect.Type;
2221
import java.net.URI;
2322
import java.security.AccessController;
@@ -64,6 +63,7 @@
6463
import org.glassfish.jersey.internal.util.collection.Ref;
6564
import org.glassfish.jersey.internal.util.collection.Value;
6665
import org.glassfish.jersey.internal.util.collection.Values;
66+
import org.glassfish.jersey.message.internal.EntityInputStream;
6767
import org.glassfish.jersey.message.internal.HeaderValueException;
6868
import org.glassfish.jersey.message.internal.MediaTypes;
6969
import org.glassfish.jersey.process.internal.RequestScoped;
@@ -78,6 +78,7 @@
7878
import org.glassfish.jersey.servlet.internal.PersistenceUnitBinder;
7979
import org.glassfish.jersey.servlet.internal.ResponseWriter;
8080
import org.glassfish.jersey.servlet.internal.ServletContainerProviderFactory;
81+
import org.glassfish.jersey.servlet.internal.ServletRequestEntityWrapper;
8182
import org.glassfish.jersey.servlet.internal.Utils;
8283
import org.glassfish.jersey.servlet.internal.spi.ExtendedServletContainerProvider;
8384
import org.glassfish.jersey.servlet.internal.spi.RequestContextProvider;
@@ -421,11 +422,7 @@ private void initContainerRequest(
421422
final HttpServletResponse servletResponse,
422423
final ResponseWriter responseWriter) throws IOException {
423424

424-
try {
425-
requestContext.setEntityStream(servletRequest.getInputStream());
426-
} catch (UncheckedIOException e) {
427-
throw e.getCause();
428-
}
425+
requestContext.wrapEntityInputStream(getInputStream(servletRequest));
429426

430427
requestContext.setRequestScopedInitializer(requestScopedInitializer.get(new RequestContextProvider() {
431428
@Override
@@ -446,6 +443,10 @@ public HttpServletResponse getHttpServletResponse() {
446443
filterFormParameters(servletRequest, requestContext);
447444
}
448445

446+
private EntityInputStream getInputStream(HttpServletRequest request) {
447+
return new ServletRequestEntityWrapper(request).getWrappedInputStream();
448+
}
449+
449450
/**
450451
* Get default {@link jakarta.ws.rs.core.SecurityContext} for given {@code request}.
451452
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.servlet.internal;
18+
19+
import jakarta.servlet.ReadListener;
20+
import jakarta.servlet.ServletInputStream;
21+
import org.glassfish.jersey.message.internal.EntityInputStream;
22+
import org.glassfish.jersey.message.internal.EntityInputStreamListener;
23+
24+
import java.io.IOException;
25+
import java.io.UncheckedIOException;
26+
27+
public abstract class ServletEntityInputStream extends ServletInputStream {
28+
29+
protected EntityInputStream wrappedStream;
30+
31+
32+
protected abstract ServletInputStream getServletInputStream();
33+
34+
@Override
35+
public boolean isFinished() {
36+
return getServletInputStream().isFinished();
37+
}
38+
39+
@Override
40+
public boolean isReady() {
41+
return getServletInputStream().isReady();
42+
}
43+
44+
@Override
45+
public void setReadListener(ReadListener readListener) {
46+
getServletInputStream().setReadListener(readListener);
47+
}
48+
49+
@Override
50+
public int read() throws IOException {
51+
return getServletInputStream().read();
52+
}
53+
54+
public EntityInputStream getWrappedStream() {
55+
if (wrappedStream == null) {
56+
wrappedStream = new EntityInputStream(getServletInputStream());
57+
wrappedStream.setListener(new EntityInputStreamListener() {
58+
@Override
59+
public boolean isEmpty() {
60+
try {
61+
return getServletInputStream().available() == 0
62+
|| getServletInputStream().isFinished();
63+
} catch (IOException e) {
64+
throw new UncheckedIOException(e);
65+
}
66+
}
67+
68+
@Override
69+
public boolean isReady() {
70+
return getServletInputStream().isReady();
71+
}
72+
});
73+
}
74+
75+
return wrappedStream;
76+
}
77+
78+
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.servlet.internal;
18+
19+
import jakarta.servlet.ServletInputStream;
20+
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletRequestWrapper;
22+
import org.glassfish.jersey.message.internal.EntityInputStream;
23+
24+
import java.io.IOException;
25+
import java.io.UncheckedIOException;
26+
27+
public class ServletRequestEntityWrapper extends HttpServletRequestWrapper {
28+
29+
private final ServletEntityInputStream servletInputStream = new ServletEntityInputStream() {
30+
@Override
31+
protected ServletInputStream getServletInputStream() {
32+
try {
33+
return getRequest().getInputStream();
34+
} catch (IOException e) {
35+
throw new UncheckedIOException(e);
36+
}
37+
}
38+
};
39+
40+
public ServletRequestEntityWrapper(HttpServletRequest request) {
41+
super(request);
42+
}
43+
44+
@Override
45+
public ServletInputStream getInputStream() throws IOException {
46+
return servletInputStream;
47+
}
48+
49+
public EntityInputStream getWrappedInputStream() {
50+
return servletInputStream.getWrappedStream();
51+
}
52+
53+
}

Diff for: core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java

+37-8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
public class EntityInputStream extends InputStreamWrapper {
3838

3939
private InputStream input;
40+
41+
private EntityInputStreamListener listener;
42+
4043
private boolean closed = false;
4144

4245
/**
@@ -131,14 +134,8 @@ public boolean isEmpty() {
131134
return i == -1;
132135
} else {
133136
int availableBytes = 0;
134-
int exceedCount = 50;
135137
try {
136-
137-
while (availableBytes == 0 && exceedCount > 0) {
138-
availableBytes = input.available();
139-
exceedCount--;
140-
}
141-
138+
availableBytes = input.available();
142139
} catch (IOException ioe) {
143140
// NOOP. Try other approaches as this can fail on WLS.
144141
}
@@ -147,6 +144,13 @@ public boolean isEmpty() {
147144
return false;
148145
}
149146

147+
if (listener != null) {
148+
if (!listener.isReady()) {
149+
return false;
150+
}
151+
return listener.isEmpty();
152+
}
153+
150154
final PushbackInputStream in = (input instanceof PushbackInputStream) ? (PushbackInputStream) input
151155
: new PushbackInputStream(input);
152156
//This situation occurs in rare cases - stream comes very late, stream's implementation does not
@@ -187,7 +191,7 @@ public boolean isClosed() {
187191
* @return wrapped input stream instance.
188192
*/
189193
public final InputStream getWrappedStream() {
190-
return input;
194+
return getWrapped();
191195
}
192196

193197
/**
@@ -203,4 +207,29 @@ public final void setWrappedStream(InputStream wrapped) {
203207
protected InputStream getWrapped() {
204208
return input;
205209
}
210+
211+
/**
212+
* Sets listener for the underlying {@link InputStream}
213+
* @param listener instance of the {@link EntityInputStreamListener}
214+
*/
215+
public void setListener(EntityInputStreamListener listener) {
216+
this.listener = listener;
217+
}
218+
219+
/**
220+
* retrieves a listener if any
221+
* @return an instance of the {@link EntityInputStreamListener}
222+
*/
223+
public EntityInputStreamListener getListener() {
224+
return listener;
225+
}
226+
227+
/**
228+
* Decomposes existing {@link EntityInputStream} into this input stream
229+
* @param stream instance of the {@link EntityInputStream}
230+
*/
231+
public void wrapEntityInputStream(EntityInputStream stream) {
232+
input = stream.getWrapped();
233+
listener = stream.getListener();
234+
}
206235
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.message.internal;
18+
19+
import java.util.EventListener;
20+
21+
/**
22+
* Provides possibility to externally check whether an input stream for an entity is empty or not.
23+
*
24+
* Is being used in the {@link EntityInputStream#isEmpty()} check
25+
*/
26+
public interface EntityInputStreamListener extends EventListener {
27+
28+
/**
29+
* Provides information if the underlying stream is empty
30+
*
31+
* @return true if the underlying stream is empty
32+
*/
33+
boolean isEmpty();
34+
35+
/**
36+
* Can be used to provide readiness information.
37+
* <p>
38+
* If the stream is not ready the calling check in the {@link EntityInputStream#isEmpty()} method will validate
39+
* the underlying stream as not empty.
40+
* </p>
41+
* @return true if the underlying stream is ready.
42+
*/
43+
boolean isReady();
44+
}

Diff for: core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java

+10
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,16 @@ public void setEntityStream(InputStream input) {
579579
this.entityContent.setContent(input, false);
580580
}
581581

582+
/**
583+
* Provides the whole {@link EntityInputStream} to the request
584+
*
585+
* @param stream the whole input stream entity
586+
*/
587+
public void wrapEntityInputStream(EntityInputStream stream) {
588+
this.entityContent.wrapEntityInputStream(stream);
589+
this.entityContent.buffered = false;
590+
}
591+
582592
/**
583593
* Read entity from a context entity input stream.
584594
*

Diff for: core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -57,6 +57,7 @@
5757
import org.glassfish.jersey.internal.util.collection.Value;
5858
import org.glassfish.jersey.internal.util.collection.Values;
5959
import org.glassfish.jersey.message.internal.AcceptableMediaType;
60+
import org.glassfish.jersey.message.internal.EntityInputStream;
6061
import org.glassfish.jersey.message.internal.HttpHeaderReader;
6162
import org.glassfish.jersey.message.internal.InboundMessageContext;
6263
import org.glassfish.jersey.message.internal.LanguageTag;
@@ -546,6 +547,12 @@ public void setEntityStream(final InputStream input) {
546547
super.setEntityStream(input);
547548
}
548549

550+
@Override
551+
public void wrapEntityInputStream(final EntityInputStream input) {
552+
Preconditions.checkState(!inResponseProcessingPhase, ERROR_REQUEST_SET_ENTITY_STREAM_IN_RESPONSE_PHASE);
553+
super.wrapEntityInputStream(input);
554+
}
555+
549556
@Override
550557
public Request getRequest() {
551558
return this;

0 commit comments

Comments
 (0)