Skip to content

Commit 029fbe2

Browse files
committed
Remove Serializable from BlobRead and BlobWriteChannel
- remove serializable from interfaces - add State interface and save method to channels - add StateImpl class to channel implementations - add tests
1 parent 7115734 commit 029fbe2

File tree

7 files changed

+443
-37
lines changed

7 files changed

+443
-37
lines changed

gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannel.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.io.Closeable;
2020
import java.io.IOException;
21-
import java.io.Serializable;
2221
import java.nio.channels.ReadableByteChannel;
2322

2423
/**
@@ -28,7 +27,7 @@
2827
*
2928
* This class is @{link Serializable}, which allows incremental reads.
3029
*/
31-
public interface BlobReadChannel extends ReadableByteChannel, Serializable, Closeable {
30+
public interface BlobReadChannel extends ReadableByteChannel, Closeable {
3231

3332
/**
3433
* Overridden to remove IOException.
@@ -46,4 +45,27 @@ public interface BlobReadChannel extends ReadableByteChannel, Serializable, Clos
4645
*/
4746
void chunkSize(int chunkSize);
4847

48+
/**
49+
* Saves the read channel state.
50+
*
51+
* @return an object that contains the read channel state and can restore it afterwards. State
52+
* object must implement {@link java.io.Serializable}.
53+
*/
54+
public State save();
55+
56+
/**
57+
* A common interface for all classes that implement the internal state of a
58+
* {@code BlobReadChannel}.
59+
*
60+
* Implementations of this class must implement {@link java.io.Serializable} to ensure that the
61+
* state of a channel can be correctly serialized.
62+
*/
63+
public interface State {
64+
65+
/**
66+
* Returns a {@code BlobReadChannel} whose internal state reflects the one saved in the
67+
* invocation object.
68+
*/
69+
public BlobReadChannel restore();
70+
}
4971
}

gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannelImpl.java

Lines changed: 129 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
import static com.google.gcloud.RetryHelper.runWithRetries;
2020

2121
import com.google.api.services.storage.model.StorageObject;
22+
import com.google.common.base.MoreObjects;
2223
import com.google.gcloud.RetryHelper;
2324
import com.google.gcloud.spi.StorageRpc;
2425

2526
import java.io.IOException;
26-
import java.io.ObjectInputStream;
27-
import java.io.ObjectOutputStream;
27+
import java.io.Serializable;
2828
import java.nio.ByteBuffer;
2929
import java.util.Map;
30+
import java.util.Objects;
3031
import java.util.concurrent.Callable;
3132

3233
/**
@@ -35,7 +36,6 @@
3536
class BlobReadChannelImpl implements BlobReadChannel {
3637

3738
private static final int DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024;
38-
private static final long serialVersionUID = 4821762590742862669L;
3939

4040
private final StorageOptions serviceOptions;
4141
private final BlobId blob;
@@ -45,10 +45,10 @@ class BlobReadChannelImpl implements BlobReadChannel {
4545
private boolean endOfStream;
4646
private int chunkSize = DEFAULT_CHUNK_SIZE;
4747

48-
private transient StorageRpc storageRpc;
49-
private transient StorageObject storageObject;
50-
private transient int bufferPos;
51-
private transient byte[] buffer;
48+
private StorageRpc storageRpc;
49+
private StorageObject storageObject;
50+
private int bufferPos;
51+
private byte[] buffer;
5252

5353
BlobReadChannelImpl(StorageOptions serviceOptions, BlobId blob,
5454
Map<StorageRpc.Option, ?> requestOptions) {
@@ -59,19 +59,18 @@ class BlobReadChannelImpl implements BlobReadChannel {
5959
initTransients();
6060
}
6161

62-
private void writeObject(ObjectOutputStream out) throws IOException {
62+
@Override
63+
public State save() {
64+
StateImpl.Builder builder = StateImpl.builder(serviceOptions, blob, requestOptions)
65+
.position(position)
66+
.isOpen(isOpen)
67+
.endOfStream(endOfStream)
68+
.chunkSize(chunkSize);
6369
if (buffer != null) {
64-
position += bufferPos;
65-
buffer = null;
66-
bufferPos = 0;
67-
endOfStream = false;
70+
builder.position(position + bufferPos);
71+
builder.endOfStream(false);
6872
}
69-
out.defaultWriteObject();
70-
}
71-
72-
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
73-
in.defaultReadObject();
74-
initTransients();
73+
return builder.build();
7574
}
7675

7776
private void initTransients() {
@@ -148,4 +147,116 @@ public byte[] call() {
148147
}
149148
return toWrite;
150149
}
150+
151+
static class StateImpl implements BlobReadChannel.State, Serializable {
152+
153+
private static final long serialVersionUID = 3889420316004453706L;
154+
155+
private final StorageOptions serviceOptions;
156+
private final BlobId blob;
157+
private final Map<StorageRpc.Option, ?> requestOptions;
158+
private final int position;
159+
private final boolean isOpen;
160+
private final boolean endOfStream;
161+
private final int chunkSize;
162+
163+
StateImpl(Builder builder) {
164+
this.serviceOptions = builder.serviceOptions;
165+
this.blob = builder.blob;
166+
this.requestOptions = builder.requestOptions;
167+
this.position = builder.position;
168+
this.isOpen = builder.isOpen;
169+
this.endOfStream = builder.endOfStream;
170+
this.chunkSize = builder.chunkSize;
171+
}
172+
173+
public static class Builder {
174+
private final StorageOptions serviceOptions;
175+
private final BlobId blob;
176+
private final Map<StorageRpc.Option, ?> requestOptions;
177+
private int position;
178+
private boolean isOpen;
179+
private boolean endOfStream;
180+
private int chunkSize;
181+
182+
private Builder(StorageOptions options, BlobId blob, Map<StorageRpc.Option, ?> reqOptions) {
183+
this.serviceOptions = options;
184+
this.blob = blob;
185+
this.requestOptions = reqOptions;
186+
}
187+
188+
public Builder position(int position) {
189+
this.position = position;
190+
return this;
191+
}
192+
193+
public Builder isOpen(boolean isOpen) {
194+
this.isOpen = isOpen;
195+
return this;
196+
}
197+
198+
public Builder endOfStream(boolean endOfStream) {
199+
this.endOfStream = endOfStream;
200+
return this;
201+
}
202+
203+
public Builder chunkSize(int chunkSize) {
204+
this.chunkSize = chunkSize;
205+
return this;
206+
}
207+
208+
public State build() {
209+
return new StateImpl(this);
210+
}
211+
}
212+
213+
public static Builder builder(
214+
StorageOptions options, BlobId blob, Map<StorageRpc.Option, ?> reqOptions) {
215+
return new Builder(options, blob, reqOptions);
216+
}
217+
218+
@Override
219+
public BlobReadChannel restore() {
220+
BlobReadChannelImpl channel = new BlobReadChannelImpl(serviceOptions, blob, requestOptions);
221+
channel.position = position;
222+
channel.isOpen = isOpen;
223+
channel.endOfStream = endOfStream;
224+
channel.chunkSize = chunkSize;
225+
return channel;
226+
}
227+
228+
@Override
229+
public int hashCode() {
230+
return Objects.hash(serviceOptions, blob, requestOptions, position, isOpen, endOfStream,
231+
chunkSize);
232+
}
233+
234+
@Override
235+
public boolean equals(Object obj) {
236+
if (obj == null) {
237+
return false;
238+
}
239+
if (!(obj instanceof StateImpl)) {
240+
return false;
241+
}
242+
final StateImpl other = (StateImpl) obj;
243+
return Objects.equals(this.serviceOptions, other.serviceOptions) &&
244+
Objects.equals(this.blob, other.blob) &&
245+
Objects.equals(this.requestOptions, other.requestOptions) &&
246+
this.position == other.position &&
247+
this.isOpen == other.isOpen &&
248+
this.endOfStream == other.endOfStream &&
249+
this.chunkSize == other.chunkSize;
250+
}
251+
252+
@Override
253+
public String toString() {
254+
return MoreObjects.toStringHelper(this)
255+
.add("blob", blob)
256+
.add("position", position)
257+
.add("isOpen", isOpen)
258+
.add("endOfStream", endOfStream)
259+
.toString();
260+
}
261+
}
151262
}

gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriteChannel.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.google.gcloud.storage;
1818

1919
import java.io.Closeable;
20-
import java.io.Serializable;
2120
import java.nio.channels.WritableByteChannel;
2221

2322
/**
@@ -27,11 +26,38 @@
2726
* data will only be visible after calling {@link #close()}. This class is serializable, to allow
2827
* incremental writes.
2928
*/
30-
public interface BlobWriteChannel extends WritableByteChannel, Serializable, Closeable {
29+
public interface BlobWriteChannel extends WritableByteChannel, Closeable {
3130

3231
/**
3332
* Sets the minimum size that will be written by a single RPC.
3433
* Written data will be buffered and only flushed upon reaching this size or closing the channel.
3534
*/
3635
void chunkSize(int chunkSize);
36+
37+
/**
38+
* Saves the write channel state.
39+
*
40+
* @return an object that contains the write channel state and can restore it afterwards. State
41+
* object must implement {@link java.io.Serializable}.
42+
*/
43+
public State save();
44+
45+
/**
46+
* A common interface for all classes that implement the internal state of a
47+
* {@code BlobWriteChannel}.
48+
*
49+
* Implementations of this class must implement {@link java.io.Serializable} to ensure that the
50+
* state of a channel can be correctly serialized.
51+
*/
52+
public interface State {
53+
54+
/**
55+
* Returns a {@code BlobWriteChannel} whose internal state reflects the one saved in the
56+
* invocation object.
57+
*
58+
* The original {@code BlobWriteChannel} and the restored one should not both be used. Closing
59+
* one channel causes the other channel to close, subsequent writes will fail.
60+
*/
61+
public BlobWriteChannel restore();
62+
}
3763
}

0 commit comments

Comments
 (0)