a4b557262eb3e885710c151bb49ec627e5520b88
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; |