Skip to content

Commit 851a045

Browse files
committed
Created FlushedCloseable interface to be passed to Context#setOutputStream
Signed-off-by: jansupol <[email protected]>
1 parent 6da18bd commit 851a045

File tree

5 files changed

+141
-14
lines changed

5 files changed

+141
-14
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
/**
18+
* Common Jersey core io classes.
19+
*/
20+
package org.glassfish.jersey.io;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.io.spi;
18+
19+
import java.io.Closeable;
20+
import java.io.Flushable;
21+
import java.io.IOException;
22+
import java.io.OutputStream;
23+
24+
/**
25+
* A marker interface that the stream provided to Jersey can implement,
26+
* noting that the stream does not need to call {@link #flush()} prior to {@link #close()}.
27+
* That way, {@link #flush()} method is not called twice.
28+
*
29+
* <p>
30+
* Usable by {@link javax.ws.rs.client.ClientRequestContext#setEntityStream(OutputStream)}.
31+
* Usable by {@link javax.ws.rs.container.ContainerResponseContext#setEntityStream(OutputStream)}.
32+
* </p>
33+
*
34+
* <p>
35+
* This marker interface can be useful for the customer OutputStream to know the {@code flush} did not come from
36+
* Jersey before close. By default, when the entity stream is to be closed by Jersey, {@code flush} is called first.
37+
* </p>
38+
*/
39+
public interface FlushedCloseable extends Flushable, Closeable {
40+
/**
41+
* Flushes this stream by writing any buffered output to the underlying stream.
42+
* Then closes this stream and releases any system resources associated
43+
* with it. If the stream is already closed then invoking this
44+
* method has no effect.
45+
*
46+
* <p> As noted in {@link AutoCloseable#close()}, cases where the
47+
* close may fail require careful attention. It is strongly advised
48+
* to relinquish the underlying resources and to internally
49+
* <em>mark</em> the {@code Closeable} as closed, prior to throwing
50+
* the {@code IOException}.
51+
*
52+
* @throws IOException if an I/O error occurs
53+
*/
54+
public void close() throws IOException;
55+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
/**
18+
* Common Jersey core io SPI classes.
19+
*/
20+
package org.glassfish.jersey.io.spi;

core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,43 +20,33 @@
2020
import java.io.OutputStream;
2121
import java.lang.annotation.Annotation;
2222
import java.lang.reflect.Type;
23-
import java.net.URI;
24-
import java.text.ParseException;
2523
import java.util.ArrayList;
2624
import java.util.Collections;
27-
import java.util.Date;
28-
import java.util.HashMap;
2925
import java.util.HashSet;
3026
import java.util.List;
3127
import java.util.Locale;
32-
import java.util.Map;
3328
import java.util.Set;
3429
import java.util.function.Function;
3530
import java.util.logging.Level;
3631
import java.util.logging.Logger;
3732
import java.util.stream.Collectors;
3833

39-
import javax.ws.rs.ProcessingException;
4034
import javax.ws.rs.core.Configuration;
41-
import javax.ws.rs.core.Cookie;
42-
import javax.ws.rs.core.EntityTag;
4335
import javax.ws.rs.core.GenericEntity;
4436
import javax.ws.rs.core.GenericType;
4537
import javax.ws.rs.core.HttpHeaders;
4638
import javax.ws.rs.core.Link;
4739
import javax.ws.rs.core.MediaType;
4840
import javax.ws.rs.core.MultivaluedMap;
49-
import javax.ws.rs.core.NewCookie;
50-
import javax.ws.rs.ext.RuntimeDelegate;
5141

5242
import org.glassfish.jersey.CommonProperties;
5343
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
54-
import org.glassfish.jersey.internal.LocalizationMessages;
5544
import org.glassfish.jersey.internal.util.ReflectionHelper;
5645
import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
5746
import org.glassfish.jersey.internal.util.collection.LazyValue;
5847
import org.glassfish.jersey.internal.util.collection.Value;
5948
import org.glassfish.jersey.internal.util.collection.Values;
49+
import org.glassfish.jersey.io.spi.FlushedCloseable;
6050

6151
/**
6252
* Base outbound message context implementation.
@@ -572,11 +562,13 @@ public void close() {
572562
if (hasEntity()) {
573563
try {
574564
final OutputStream es = getEntityStream();
575-
es.flush();
565+
if (!FlushedCloseable.class.isInstance(es)) {
566+
es.flush();
567+
}
576568
es.close();
577569
} catch (IOException e) {
578570
// Happens when the client closed connection before receiving the full response.
579-
// This is OK and not interesting in vast majority of the cases
571+
// This is OK and not interesting in the vast majority of the cases
580572
// hence the log level set to FINE to make sure it does not flood the log unnecessarily
581573
// (especially for clients disconnecting from SSE listening, which is very common).
582574
Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e);

tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java

Lines changed: 41 additions & 1 deletion
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
@@ -16,6 +16,9 @@
1616

1717
package org.glassfish.jersey.tests.e2e.common.message.internal;
1818

19+
import java.io.ByteArrayOutputStream;
20+
import java.io.IOException;
21+
import java.io.OutputStream;
1922
import java.net.URI;
2023
import java.net.URISyntaxException;
2124
import java.text.ParseException;
@@ -33,10 +36,13 @@
3336
import javax.ws.rs.core.MediaType;
3437
import javax.ws.rs.ext.RuntimeDelegate;
3538

39+
import org.glassfish.jersey.io.spi.FlushedCloseable;
3640
import org.glassfish.jersey.message.internal.CookieProvider;
3741
import org.glassfish.jersey.message.internal.OutboundMessageContext;
3842
import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
3943

44+
import org.hamcrest.MatcherAssert;
45+
import org.hamcrest.Matchers;
4046
import org.junit.jupiter.api.Assertions;
4147
import org.junit.jupiter.api.Test;
4248
import static org.hamcrest.Matchers.contains;
@@ -271,4 +277,38 @@ public void testCopyConstructor() {
271277
newCtx.setMediaType(MediaType.APPLICATION_XML_TYPE); // new value
272278
Assertions.assertEquals(MediaType.APPLICATION_XML_TYPE, newCtx.getMediaType());
273279
}
280+
281+
@Test
282+
public void OutboundMessageContextFlushTest() throws IOException {
283+
FlushCountOutputStream os = new FlushCountOutputStream();
284+
OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
285+
ctx.setEntity("Anything");
286+
ctx.setEntityStream(os);
287+
os.flush();
288+
ctx.close();
289+
MatcherAssert.assertThat(os.flushedCnt, Matchers.is(2));
290+
291+
os = new FlushedClosableOutputStream();
292+
ctx = new OutboundMessageContext((Configuration) null);
293+
ctx.setEntity("Anything2");
294+
ctx.setEntityStream(os);
295+
os.flush();
296+
ctx.close();
297+
MatcherAssert.assertThat(os.flushedCnt, Matchers.is(1));
298+
}
299+
300+
private static class FlushCountOutputStream extends ByteArrayOutputStream {
301+
private int flushedCnt = 0;
302+
303+
@Override
304+
public void flush() throws IOException {
305+
flushedCnt++;
306+
super.flush();
307+
}
308+
}
309+
310+
private static class FlushedClosableOutputStream extends FlushCountOutputStream implements FlushedCloseable {
311+
312+
}
274313
}
314+

0 commit comments

Comments
 (0)