java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractSerializationTest.java
... ...
@@ -11,7 +11,7 @@ import java.util.HashMap;
11 11
import com.sap.sailing.domain.base.DomainFactory;
12 12
13 13
public abstract class AbstractSerializationTest {
14
- static <T extends Serializable> T cloneBySerialization(final T s, DomainFactory resolveAgainst) throws IOException, ClassNotFoundException {
14
+ protected static <T extends Serializable> T cloneBySerialization(final T s, DomainFactory resolveAgainst) throws IOException, ClassNotFoundException {
15 15
PipedOutputStream pos = new PipedOutputStream();
16 16
PipedInputStream pis = new PipedInputStream(pos);
17 17
final ObjectOutputStream dos = new ObjectOutputStream(pos);
... ...
@@ -27,7 +27,8 @@ public abstract class AbstractSerializationTest {
27 27
}
28 28
}.start();
29 29
Thread.currentThread().setContextClassLoader(AbstractSerializationTest.class.getClassLoader());
30
- ObjectInputStream dis = resolveAgainst.createObjectInputStreamResolvingAgainstThisFactory(pis, /* resolve listener */ null, /* classLoaderCache */ new HashMap<>());
30
+ ObjectInputStream dis = resolveAgainst == null ? new ObjectInputStream(pis) :
31
+ resolveAgainst.createObjectInputStreamResolvingAgainstThisFactory(pis, /* resolve listener */ null, /* classLoaderCache */ new HashMap<>());
31 32
@SuppressWarnings("unchecked")
32 33
T result = (T) dis.readObject();
33 34
dis.close();
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubsequenceFinderLinkedNodesImpl.java
... ...
@@ -1,5 +1,8 @@
1 1
package com.sap.sse.common.scalablevalue;
2 2
3
+import java.io.IOException;
4
+import java.io.ObjectInputStream;
5
+import java.io.ObjectOutputStream;
3 6
import java.io.Serializable;
4 7
import java.util.Collections;
5 8
import java.util.Iterator;
... ...
@@ -28,9 +31,9 @@ public class KadaneExtremeSubsequenceFinderLinkedNodesImpl<ValueType, AveragesTo
28 31
29 32
private static final long serialVersionUID = -8986609116472739636L;
30 33
31
- private Node<ValueType, AveragesTo, T> first;
34
+ private transient Node<ValueType, AveragesTo, T> first;
32 35
33
- private Node<ValueType, AveragesTo, T> last;
36
+ private transient Node<ValueType, AveragesTo, T> last;
34 37
35 38
private int size;
36 39
... ...
@@ -63,12 +66,12 @@ public class KadaneExtremeSubsequenceFinderLinkedNodesImpl<ValueType, AveragesTo
63 66
private static int idCounter = 0;
64 67
private final T value;
65 68
private final int id;
66
- private Node<ValueType, AveragesTo, T> previous;
67
- private Node<ValueType, AveragesTo, T> next;
69
+ private transient Node<ValueType, AveragesTo, T> previous;
70
+ private transient Node<ValueType, AveragesTo, T> next;
68 71
private ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingHere;
69
- private Node<ValueType, AveragesTo, T> startOfMinSumSubSequenceEndingHere;
72
+ private transient Node<ValueType, AveragesTo, T> startOfMinSumSubSequenceEndingHere;
70 73
private ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingHere;
71
- private Node<ValueType, AveragesTo, T> startOfMaxSumSubSequenceEndingHere;
74
+ private transient Node<ValueType, AveragesTo, T> startOfMaxSumSubSequenceEndingHere;
72 75
73 76
private Node(Node<ValueType, AveragesTo, T> previous, Node<ValueType, AveragesTo, T> next, T value) {
74 77
super();
... ...
@@ -246,6 +249,57 @@ public class KadaneExtremeSubsequenceFinderLinkedNodesImpl<ValueType, AveragesTo
246 249
this.nodesOrderedByMaxSum = new TreeSet<>(maxSumOuterComparator);
247 250
}
248 251
252
+ /**
253
+ * Writes the sequence iteratively instead of by recursion, which would be the default
254
+ * {@link ObjectOutputStream} behavior. The nodes need to be "wired" again by the reading end
255
+ * regarding the {@link #previous} and {@link #next} links based on the order in which the
256
+ * nodes are written to the stream.<p>
257
+ *
258
+ * After the nodes follow two references per node to the start nodes of the extreme sum sub-sequences ending at that node,
259
+ * as those are transient again to avoid recursion, and thus not written by the default serialization process. We can assume
260
+ * that the {@link ObjectOutputStream} already holds those nodes, and so only references to those nodes need to be written.
261
+ */
262
+ private void writeObject(ObjectOutputStream oos) throws IOException {
263
+ oos.defaultWriteObject();
264
+ final Iterable<Node<ValueType, AveragesTo, T>> iterable = this::nodeIterator;
265
+ for (final Node<ValueType, AveragesTo, T> node : iterable) {
266
+ oos.writeObject(node);
267
+ }
268
+ // writing the start nodes of the extreme sum sub-sequences separately, as they are transient and thus not written by the default serialization process;
269
+ // we need to write them separately as otherwise we would lose the references to those nodes, and thus the information about where the extreme sum sub-sequences start
270
+ for (final Node<ValueType, AveragesTo, T> node : iterable) {
271
+ oos.writeObject(node.getStartOfMinSumSubSequenceEndingHere());
272
+ oos.writeObject(node.getStartOfMaxSumSubSequenceEndingHere());
273
+ }
274
+ }
275
+
276
+ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
277
+ ois.defaultReadObject(); // this is expected to read the size field
278
+ Node<ValueType, AveragesTo, T> lastRead = null;
279
+ for (int i=0; i<size; i++) {
280
+ @SuppressWarnings("unchecked")
281
+ final Node<ValueType, AveragesTo, T> node = (Node<ValueType, AveragesTo, T>) ois.readObject();
282
+ node.setPrevious(lastRead);
283
+ if (lastRead != null) {
284
+ lastRead.setNext(node);
285
+ }
286
+ lastRead = node;
287
+ if (first == null) {
288
+ first = lastRead;
289
+ }
290
+ }
291
+ last = lastRead;
292
+ final Iterable<Node<ValueType, AveragesTo, T>> iterable = this::nodeIterator;
293
+ for (final Node<ValueType, AveragesTo, T> node : iterable) {
294
+ @SuppressWarnings("unchecked")
295
+ final Node<ValueType, AveragesTo, T> startOfMinSubSubSequenceEndingHere = (Node<ValueType, AveragesTo, T>) ois.readObject();
296
+ node.setStartOfMinSumSubSequenceEndingHere(startOfMinSubSubSequenceEndingHere);
297
+ @SuppressWarnings("unchecked")
298
+ final Node<ValueType, AveragesTo, T> startOfMaxSubSubSequenceEndingHere = (Node<ValueType, AveragesTo, T>) ois.readObject();
299
+ node.setStartOfMaxSumSubSequenceEndingHere(startOfMaxSubSubSequenceEndingHere);
300
+ }
301
+ }
302
+
249 303
@Override
250 304
public int size() {
251 305
return size;