Skip to content

Commit

Permalink
EntityInputStream#isEmpty refactoring
Browse files Browse the repository at this point in the history
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
  • Loading branch information
senivam committed Dec 11, 2024
1 parent 318e355 commit e90402b
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.glassfish.jersey.servlet;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.security.AccessController;
Expand Down Expand Up @@ -64,6 +63,7 @@
import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.message.internal.EntityInputStream;
import org.glassfish.jersey.message.internal.HeaderValueException;
import org.glassfish.jersey.message.internal.MediaTypes;
import org.glassfish.jersey.process.internal.RequestScoped;
Expand All @@ -78,6 +78,7 @@
import org.glassfish.jersey.servlet.internal.PersistenceUnitBinder;
import org.glassfish.jersey.servlet.internal.ResponseWriter;
import org.glassfish.jersey.servlet.internal.ServletContainerProviderFactory;
import org.glassfish.jersey.servlet.internal.ServletRequestEntityWrapper;
import org.glassfish.jersey.servlet.internal.Utils;
import org.glassfish.jersey.servlet.internal.spi.ExtendedServletContainerProvider;
import org.glassfish.jersey.servlet.internal.spi.RequestContextProvider;
Expand Down Expand Up @@ -421,11 +422,7 @@ private void initContainerRequest(
final HttpServletResponse servletResponse,
final ResponseWriter responseWriter) throws IOException {

try {
requestContext.setEntityStream(servletRequest.getInputStream());
} catch (UncheckedIOException e) {
throw e.getCause();
}
requestContext.wrapEntityInputStream(getInputStream(servletRequest));

requestContext.setRequestScopedInitializer(requestScopedInitializer.get(new RequestContextProvider() {
@Override
Expand All @@ -446,6 +443,10 @@ public HttpServletResponse getHttpServletResponse() {
filterFormParameters(servletRequest, requestContext);
}

private EntityInputStream getInputStream(HttpServletRequest request) {
return new ServletRequestEntityWrapper(request).getWrappedInputStream();
}

/**
* Get default {@link jakarta.ws.rs.core.SecurityContext} for given {@code request}.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.servlet.internal;

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import org.glassfish.jersey.message.internal.EntityInputStream;
import org.glassfish.jersey.message.internal.EntityInputStreamListener;

import java.io.IOException;
import java.io.UncheckedIOException;

public abstract class ServletEntityInputStream extends ServletInputStream {

protected EntityInputStream wrappedStream;


protected abstract ServletInputStream getServletInputStream();

@Override
public boolean isFinished() {
return getServletInputStream().isFinished();
}

@Override
public boolean isReady() {
return getServletInputStream().isReady();
}

@Override
public void setReadListener(ReadListener readListener) {
getServletInputStream().setReadListener(readListener);
}

@Override
public int read() throws IOException {
return getServletInputStream().read();
}

public EntityInputStream getWrappedStream() {
if (wrappedStream == null) {
wrappedStream = new EntityInputStream(getServletInputStream());
wrappedStream.setListener(new EntityInputStreamListener() {
@Override
public boolean isEmpty() {
try {
return getServletInputStream().available() == 0
|| getServletInputStream().isFinished();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public boolean isReady() {
return getServletInputStream().isReady();
}
});
}

return wrappedStream;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.servlet.internal;

import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.glassfish.jersey.message.internal.EntityInputStream;

import java.io.IOException;
import java.io.UncheckedIOException;

public class ServletRequestEntityWrapper extends HttpServletRequestWrapper {

private final ServletEntityInputStream servletInputStream = new ServletEntityInputStream() {
@Override
protected ServletInputStream getServletInputStream() {
try {
return getRequest().getInputStream();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
};

public ServletRequestEntityWrapper(HttpServletRequest request) {
super(request);
}

@Override
public ServletInputStream getInputStream() throws IOException {
return servletInputStream;
}

public EntityInputStream getWrappedInputStream() {
return servletInputStream.getWrappedStream();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
public class EntityInputStream extends InputStreamWrapper {

private InputStream input;

private EntityInputStreamListener listener;

private boolean closed = false;

/**
Expand Down Expand Up @@ -131,14 +134,8 @@ public boolean isEmpty() {
return i == -1;
} else {
int availableBytes = 0;
int exceedCount = 50;
try {

while (availableBytes == 0 && exceedCount > 0) {
availableBytes = input.available();
exceedCount--;
}

availableBytes = input.available();
} catch (IOException ioe) {
// NOOP. Try other approaches as this can fail on WLS.
}
Expand All @@ -147,6 +144,13 @@ public boolean isEmpty() {
return false;
}

if (listener != null) {
if (!listener.isReady()) {
return false;
}
return listener.isEmpty();
}

final PushbackInputStream in = (input instanceof PushbackInputStream) ? (PushbackInputStream) input
: new PushbackInputStream(input);
//This situation occurs in rare cases - stream comes very late, stream's implementation does not
Expand Down Expand Up @@ -187,7 +191,7 @@ public boolean isClosed() {
* @return wrapped input stream instance.
*/
public final InputStream getWrappedStream() {
return input;
return getWrapped();
}

/**
Expand All @@ -203,4 +207,29 @@ public final void setWrappedStream(InputStream wrapped) {
protected InputStream getWrapped() {
return input;
}

/**
* Sets listener for the underlying {@link InputStream}
* @param listener instance of the {@link EntityInputStreamListener}
*/
public void setListener(EntityInputStreamListener listener) {
this.listener = listener;
}

/**
* retrieves a listener if any
* @return an instance of the {@link EntityInputStreamListener}
*/
public EntityInputStreamListener getListener() {
return listener;
}

/**
* Decomposes existing {@link EntityInputStream} into this input stream
* @param stream instance of the {@link EntityInputStream}
*/
public void wrapEntityInputStream(EntityInputStream stream) {
input = stream.getWrapped();
listener = stream.getListener();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.message.internal;

import java.util.EventListener;

/**
* Provides possibility to externally check whether an input stream for an entity is empty or not.
*
* Is being used in the {@link EntityInputStream#isEmpty()} check
*/
public interface EntityInputStreamListener extends EventListener {

/**
* Provides information if the underlying stream is empty
*
* @return true if the underlying stream is empty
*/
boolean isEmpty();

/**
* Can be used to provide readiness information.
* <p>
* If the stream is not ready the calling check in the {@link EntityInputStream#isEmpty()} method will validate
* the underlying stream as not empty.
* </p>
* @return true if the underlying stream is ready.
*/
boolean isReady();
}
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,16 @@ public void setEntityStream(InputStream input) {
this.entityContent.setContent(input, false);
}

/**
* Provides the whole {@link EntityInputStream} to the request
*
* @param stream the whole input stream entity
*/
public void wrapEntityInputStream(EntityInputStream stream) {
this.entityContent.wrapEntityInputStream(stream);
this.entityContent.buffered = false;
}

/**
* Read entity from a context entity input stream.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -57,6 +57,7 @@
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.message.internal.AcceptableMediaType;
import org.glassfish.jersey.message.internal.EntityInputStream;
import org.glassfish.jersey.message.internal.HttpHeaderReader;
import org.glassfish.jersey.message.internal.InboundMessageContext;
import org.glassfish.jersey.message.internal.LanguageTag;
Expand Down Expand Up @@ -546,6 +547,12 @@ public void setEntityStream(final InputStream input) {
super.setEntityStream(input);
}

@Override
public void wrapEntityInputStream(final EntityInputStream input) {
Preconditions.checkState(!inResponseProcessingPhase, ERROR_REQUEST_SET_ENTITY_STREAM_IN_RESPONSE_PHASE);
super.wrapEntityInputStream(input);
}

@Override
public Request getRequest() {
return this;
Expand Down

0 comments on commit e90402b

Please sign in to comment.