Skip to content

Commit ce36617

Browse files
authored
chore: fix BlobReadSession ReadProjectionConfigs.asSeekableChannel() close() behavior (googleapis#3047)
Update SeekableChannel close() to only execute close logic once.
1 parent 861f958 commit ce36617

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/ObjectReadSessionSeekableByteChannel.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ public boolean isOpen() {
130130

131131
@Override
132132
public void close() throws IOException {
133+
if (!open) {
134+
return;
135+
}
133136
try (IOAutoCloseable ignore1 = closeAlongWithThis;
134137
ReadableByteChannel ignore2 = rbc) {
135138
open = false;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.storage;
18+
19+
import static com.google.cloud.storage.TestUtils.assertAll;
20+
import static com.google.common.truth.Truth.assertThat;
21+
22+
import com.google.storage.v2.Object;
23+
import java.io.IOException;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
import org.junit.Test;
26+
27+
public final class ObjectReadSessionLifeCycleTest {
28+
29+
@Test
30+
public void seekableChannel_closeOnlyExecutesOnce() throws Exception {
31+
32+
AtomicInteger sessionCloseCount = new AtomicInteger(0);
33+
AtomicInteger closeAlongCount = new AtomicInteger(0);
34+
35+
ObjectReadSession session =
36+
new ObjectReadSession() {
37+
@Override
38+
public Object getResource() {
39+
return Object.getDefaultInstance();
40+
}
41+
42+
@Override
43+
public <Projection> Projection readAs(ReadProjectionConfig<Projection> config) {
44+
return null;
45+
}
46+
47+
@Override
48+
public void close() throws IOException {
49+
sessionCloseCount.getAndIncrement();
50+
}
51+
};
52+
ObjectReadSessionSeekableByteChannel channel =
53+
new ObjectReadSessionSeekableByteChannel(
54+
session,
55+
ReadAsSeekableChannel.INSTANCE,
56+
session.andThen(closeAlongCount::getAndIncrement));
57+
58+
channel.close();
59+
channel.close();
60+
channel.close();
61+
62+
assertAll(
63+
() -> assertThat(sessionCloseCount.get()).isEqualTo(1),
64+
() -> assertThat(closeAlongCount.get()).isEqualTo(1));
65+
}
66+
}

0 commit comments

Comments
 (0)