Skip to content

Commit 185a1db

Browse files
committed
Merge branch 'release/0.3.5'
2 parents 1a007bd + fffec56 commit 185a1db

File tree

11 files changed

+242
-26
lines changed

11 files changed

+242
-26
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
language: scala
22
scala:
3-
- 2.10.0
3+
- 2.10.3
44
- 2.9.3

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# chill #
22

3+
### 0.3.5
4+
* Add Serializers for scala SortedList and ListSet. https://github.com/twitter/chill/pull/152
5+
* Fix Range serialization (and remove broken subclass-serialization of Iterable, Seq, etc): https://github.com/twitter/chill/pull/154
6+
* Build and test chill-akka for 2.10: https://github.com/twitter/chill/pull/155
7+
* Add chill-thrift: https://github.com/twitter/chill/pull/156
8+
* Support JavaConverter-built classes: https://github.com/twitter/chill/pull/159
9+
310
### 0.3.4
411
* Bugfixes for Externalizer with looped object graphs https://github.com/twitter/chill/pull/143
512

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ Discussion occurs primarily on the [Chill mailing list](https://groups.google.co
147147

148148
## Maven
149149

150-
Chill modules are available on Maven Central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.3.3`.
150+
Chill modules are available on Maven Central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.3.5`.
151151

152152
Current published artifacts are
153153

chill-java/src/main/java/com/twitter/chill/KryoInstantiator.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ public KryoInstantiator setReferences(final boolean ref) {
4747
return new KryoInstantiator() {
4848
public Kryo newKryo() {
4949
Kryo k = KryoInstantiator.this.newKryo();
50-
k.setReferences(ref);
50+
/**
51+
* Kryo 2.17, used in storm, has this method returning void,
52+
* 2.21 has it returning boolean.
53+
* Try not to call the method if you don't need to.
54+
*/
55+
if(k.getReferences() != ref) { k.setReferences(ref); }
5156
return k;
5257
}
5358
};
@@ -59,7 +64,10 @@ public KryoInstantiator setRegistrationRequired(final boolean req) {
5964
return new KryoInstantiator() {
6065
public Kryo newKryo() {
6166
Kryo k = KryoInstantiator.this.newKryo();
62-
k.setRegistrationRequired(req);
67+
/** Try to avoid calling this method if you don't need to.
68+
* We've been burned by binary compatibility with Kryo
69+
*/
70+
if(k.isRegistrationRequired() != req) { k.setRegistrationRequired(req); }
6371
return k;
6472
}
6573
};

chill-scala/src/main/scala/com/twitter/chill/ScalaKryoInstantiator.scala

+25-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ package com.twitter.chill
1818

1919
import scala.collection.immutable.{
2020
BitSet,
21+
ListSet,
22+
NumericRange,
23+
Range,
24+
SortedSet,
2125
ListMap,
2226
HashMap,
2327
Queue
@@ -39,6 +43,8 @@ import scala.util.matching.Regex
3943
import com.twitter.chill.java.PackageRegistrar
4044
import _root_.java.io.Serializable
4145

46+
import scala.collection.JavaConverters._
47+
4248
/** This class has a no-arg constructor, suitable for use with reflection instantiation
4349
* It has no registered serializers, just the standard Kryo configured for Kryo.
4450
*/
@@ -83,6 +89,19 @@ class ScalaKryoInstantiator extends EmptyScalaKryoInstantiator {
8389

8490
class ScalaCollectionsRegistrar extends IKryoRegistrar {
8591
def apply(newK: Kryo) {
92+
// for binary compat this is here, but could be moved to RichKryo
93+
def useField[T](cls: Class[T]) {
94+
val fs = new com.esotericsoftware.kryo.serializers.FieldSerializer(newK, cls)
95+
fs.setIgnoreSyntheticFields(false) // scala generates a lot of these attributes
96+
newK.register(cls, fs)
97+
}
98+
// The wrappers are private classes:
99+
useField(List(1, 2, 3).asJava.getClass)
100+
useField(List(1, 2, 3).iterator.asJava.getClass)
101+
useField(Map(1 -> 2, 4 -> 3).asJava.getClass)
102+
useField(new _root_.java.util.ArrayList().asScala.getClass)
103+
useField(new _root_.java.util.HashMap().asScala.getClass)
104+
86105
/*
87106
* Note that subclass-based use: addDefaultSerializers, else: register
88107
* You should go from MOST specific, to least to specific when using
@@ -92,6 +111,7 @@ class ScalaCollectionsRegistrar extends IKryoRegistrar {
92111
// wrapper array is abstract
93112
.forSubclass[WrappedArray[Any]](new WrappedArraySerializer[Any])
94113
.forSubclass[BitSet](new BitSetSerializer)
114+
.forSubclass[SortedSet[Any]](new SortedSetSerializer)
95115
.forClass[Some[Any]](new SomeSerializer[Any])
96116
.forClass[Left[Any, Any]](new LeftSerializer[Any, Any])
97117
.forClass[Right[Any, Any]](new RightSerializer[Any, Any])
@@ -102,20 +122,23 @@ class ScalaCollectionsRegistrar extends IKryoRegistrar {
102122
.forTraversableSubclass(Buffer.empty[Any], isImmutable = false)
103123
// Vector is a final class
104124
.forTraversableClass(Vector.empty[Any])
105-
.forTraversableSubclass(IndexedSeq.empty[Any])
125+
.forTraversableSubclass(ListSet.empty[Any])
106126
// specifically register small sets since Scala represents them differently
107127
.forConcreteTraversableClass(Set[Any]('a))
108128
.forConcreteTraversableClass(Set[Any]('a, 'b))
109129
.forConcreteTraversableClass(Set[Any]('a, 'b, 'c))
110130
.forConcreteTraversableClass(Set[Any]('a, 'b, 'c, 'd))
111131
.forConcreteTraversableClass(Set[Any]('a, 'b, 'c, 'd, 'e))
112-
.forTraversableSubclass(Set.empty[Any])
113132
// specifically register small maps since Scala represents them differently
114133
.forConcreteTraversableClass(Map[Any, Any]('a -> 'a))
115134
.forConcreteTraversableClass(Map[Any, Any]('a -> 'a, 'b -> 'b))
116135
.forConcreteTraversableClass(Map[Any, Any]('a -> 'a, 'b -> 'b, 'c -> 'c))
117136
.forConcreteTraversableClass(Map[Any, Any]('a -> 'a, 'b -> 'b, 'c -> 'c, 'd -> 'd))
118137
.forConcreteTraversableClass(Map[Any, Any]('a -> 'a, 'b -> 'b, 'c -> 'c, 'd -> 'd, 'e -> 'e))
138+
// The normal fields serializer works for ranges
139+
.registerClasses(Seq(classOf[Range.Inclusive],
140+
classOf[NumericRange.Inclusive[_]],
141+
classOf[NumericRange.Exclusive[_]]))
119142
// Add some maps
120143
.forTraversableSubclass(ListMap.empty[Any,Any])
121144
.forTraversableSubclass(HashMap.empty[Any,Any])
@@ -128,12 +151,6 @@ class ScalaCollectionsRegistrar extends IKryoRegistrar {
128151
.forTraversableSubclass(MMap.empty[Any,Any], isImmutable = false)
129152
.forTraversableSubclass(MSet.empty[Any], isImmutable = false)
130153
.forTraversableSubclass(ListBuffer.empty[Any], isImmutable = false)
131-
// This should be last, lots of things are seq/iterable/traversable
132-
// These are questionable and might break things.
133-
// rarely will you only expect an iterable/traversable on the reverse
134-
.forTraversableSubclass(Seq.empty[Any])
135-
.forTraversableSubclass(Iterable.empty[Any])
136-
.forTraversableSubclass(Traversable.empty[Any])
137154
}
138155
}
139156

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2012 Twitter, Inc.
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.twitter.chill
18+
19+
import scala.collection.immutable.SortedSet
20+
21+
class SortedSetSerializer[T] extends KSerializer[SortedSet[T]] {
22+
def write(kser: Kryo, out: Output, set: SortedSet[T]) {
23+
//Write the size
24+
out.writeInt(set.size, true)
25+
26+
// Write the ordering
27+
kser.writeClassAndObject(out, set.ordering.asInstanceOf[AnyRef])
28+
set.foreach { t =>
29+
val tRef = t.asInstanceOf[AnyRef]
30+
kser.writeClassAndObject(out, tRef)
31+
// After each intermediate object, flush
32+
out.flush()
33+
}
34+
}
35+
36+
def read(kser: Kryo, in: Input, cls: Class[SortedSet[T]]): SortedSet[T] = {
37+
val size = in.readInt(true)
38+
val ordering = kser.readClassAndObject(in).asInstanceOf[Ordering[T]]
39+
40+
// Go ahead and be faster, and not as functional cool, and be mutable in here
41+
var idx = 0
42+
val builder = SortedSet.canBuildFrom[T](ordering)()
43+
builder.sizeHint(size)
44+
45+
while (idx < size) {
46+
val item = kser.readClassAndObject(in).asInstanceOf[T]
47+
builder += item
48+
idx += 1
49+
}
50+
builder.result()
51+
}
52+
}

chill-scala/src/test/scala/com/twitter/chill/KryoSpec.scala

+43-4
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ package com.twitter.chill
1818

1919
import org.specs._
2020

21-
import scala.collection.immutable.BitSet
22-
import scala.collection.immutable.ListMap
23-
import scala.collection.immutable.HashMap
24-
21+
import scala.collection.immutable.{SortedSet, BitSet, ListSet, ListMap, HashMap}
2522
import scala.collection.mutable.{ArrayBuffer => MArrayBuffer, HashMap => MHashMap}
2623
import _root_.java.util.PriorityQueue
2724
import _root_.java.util.Locale
2825
import scala.collection.mutable
26+
import scala.collection.JavaConverters._
27+
2928
/*
3029
* This is just a test case for Kryo to deal with. It should
3130
* be outside KryoSpec, otherwise the enclosing class, KryoSpec
@@ -49,6 +48,8 @@ trait ExampleUsingSelf { self =>
4948
def addOne = new ExampleUsingSelf {override def count=self.count+1}
5049
}
5150

51+
case class Foo(m1: Map[String, Int], m2: Map[String, Seq[String]])
52+
5253
class KryoSpec extends Specification with BaseProperties {
5354

5455
noDetailedDiffs() //Fixes issue for scala 2.9
@@ -60,6 +61,7 @@ class KryoSpec extends Specification with BaseProperties {
6061
val test = List(1,2,"hey",(1,2),
6162
("hey","you"),
6263
("slightly", 1L, "longer", 42, "tuple"),
64+
Foo(Map("1" -> 1), Map("1" -> Seq("foo.com"))),
6365
Map(1->2,4->5),
6466
0 to 100,
6567
(0 to 42).toList, Seq(1,100,1000),
@@ -70,6 +72,8 @@ class KryoSpec extends Specification with BaseProperties {
7072
MArrayBuffer(1,2,3,4,5),
7173
List(Some(MHashMap(1->1, 2->2)), None, Some(MHashMap(3->4))),
7274
Set(1,2,3,4,10),
75+
SortedSet[Long](),
76+
SortedSet(1L, 2L, 3L, 4L),
7377
BitSet(),
7478
BitSet((0 until 1000).map{ x : Int => x*x } : _*),
7579
ListMap("good" -> 0.5, "bad" -> -1.0),
@@ -83,6 +87,10 @@ class KryoSpec extends Specification with BaseProperties {
8387
Vector(1,2,3,4,5),
8488
TestValMap(null),
8589
Some("junk"),
90+
List(1, 2, 3).asJava,
91+
Map("hey" -> 1, "you" -> 2).asJava,
92+
new _root_.java.util.ArrayList(Seq(1, 2, 3).asJava).asScala,
93+
new _root_.java.util.HashMap[Int,Int](Map(1 -> 2, 3 -> 4).asJava).asScala,
8694
(),
8795
'hai)
8896
.asInstanceOf[List[AnyRef]]
@@ -92,6 +100,22 @@ class KryoSpec extends Specification with BaseProperties {
92100
serdeser must be_==(orig)
93101
}
94102
}
103+
"round trip a SortedSet" in {
104+
val a = SortedSet[Long]() // Test empty SortedSet
105+
val b = SortedSet[Int](1,2) // Test small SortedSet
106+
val c = SortedSet[Int](1,2,3,4,6,7,8,9,10)(Ordering.fromLessThan((x, y) => x > y)) // Test with different ordering
107+
rt(a) must be_==(a)
108+
rt(b) must be_==(b)
109+
(rt(c) + 5) must be_==(c + 5)
110+
}
111+
"round trip a ListSet" in {
112+
val a = ListSet[Long]() // Test empty SortedSet
113+
val b = ListSet[Int](1,2) // Test small ListSet
114+
val c = ListSet[Int](1,2,3,4,6,7,8,9,10)
115+
rt(a) must be_==(a)
116+
rt(b) must be_==(b)
117+
(rt(c)) must be_==(c)
118+
}
95119
"handle trait with reference of self" in {
96120
var a= new ExampleUsingSelf{}
97121
var b=rt(a.addOne)
@@ -270,5 +294,20 @@ class KryoSpec extends Specification with BaseProperties {
270294
val qrlist = toList(qr)
271295
toList(rt(qr)) must be_==(qrlist)
272296
}
297+
"Ranges should be fixed size" in {
298+
val MAX_RANGE_SIZE = 188 // what seems to be needed.
299+
serialize((1 to 10000)).size must be_<(MAX_RANGE_SIZE) // some fixed size
300+
serialize((1 to 10000 by 2)).size must be_<(MAX_RANGE_SIZE) // some fixed size
301+
serialize((1 until 10000)).size must be_<(MAX_RANGE_SIZE) // some fixed size
302+
serialize((1 until 10000 by 2)).size must be_<(MAX_RANGE_SIZE) // some fixed size
303+
serialize((1L to 10000L)).size must be_<(MAX_RANGE_SIZE) // some fixed size
304+
serialize((1L to 10000L by 2L)).size must be_<(MAX_RANGE_SIZE) // some fixed size
305+
serialize((1L until 10000L)).size must be_<(MAX_RANGE_SIZE) // some fixed size
306+
serialize((1L until 10000L by 2L)).size must be_<(MAX_RANGE_SIZE) // some fixed size
307+
serialize((1.0 to 10000.0)).size must be_<(MAX_RANGE_SIZE) // some fixed size
308+
serialize((1.0 to 10000.0 by 2.0)).size must be_<(MAX_RANGE_SIZE) // some fixed size
309+
serialize((1.0 until 10000.0)).size must be_<(MAX_RANGE_SIZE) // some fixed size
310+
serialize((1.0 until 10000.0 by 2.0)).size must be_<(MAX_RANGE_SIZE) // some fixed size
311+
}
273312
}
274313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2013 Twitter, Inc.
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.twitter.chill.thrift;
18+
19+
import com.esotericsoftware.kryo.Kryo;
20+
import com.esotericsoftware.kryo.Serializer;
21+
import com.esotericsoftware.kryo.io.Input;
22+
import com.esotericsoftware.kryo.io.Output;
23+
24+
import org.apache.thrift.TBase;
25+
import org.apache.thrift.TDeserializer;
26+
import org.apache.thrift.TException;
27+
import org.apache.thrift.TSerializer;
28+
29+
/**
30+
* Kryo serializer for Thrift instances.
31+
*
32+
* Note that this class is not thread-safe. (Kryo itself is not thread
33+
* safe, so this shouldn't be a concern.)
34+
*
35+
* Use this with
36+
* addDefaultSerializer(TBase.class, TBaseSerializer.class)
37+
* It still helps to .register your instances so the full class name
38+
* does not need to be written.
39+
*/
40+
public class TBaseSerializer extends Serializer<TBase> {
41+
private final TSerializer serializer = new TSerializer();
42+
private final TDeserializer deserializer = new TDeserializer();
43+
44+
@Override
45+
public void write(Kryo kryo, Output output, TBase tBase) {
46+
try {
47+
byte[] serThrift = serializer.serialize(tBase);
48+
output.writeInt(serThrift.length, true);
49+
output.writeBytes(serThrift);
50+
} catch (TException e) {
51+
throw new RuntimeException(e);
52+
}
53+
}
54+
55+
@Override
56+
public TBase read(Kryo kryo, Input input, Class<TBase> tBaseClass) {
57+
try {
58+
TBase prototype = tBaseClass.newInstance();
59+
int tSize = input.readInt(true);
60+
byte[] barr = new byte[tSize];
61+
input.readBytes(barr);
62+
deserializer.deserialize(prototype, barr);
63+
return prototype;
64+
} catch (Exception e) {
65+
throw new RuntimeException("Could not create " + tBaseClass, e);
66+
}
67+
}
68+
}
69+

0 commit comments

Comments
 (0)