Skip to content

Commit ba645de

Browse files
committed
Merge branch 'release/0.3.0'
2 parents caf7d36 + 3063150 commit ba645de

File tree

79 files changed

+5638
-660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+5638
-660
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
language: scala
22
scala:
33
- 2.10.0
4-
- 2.9.2
4+
- 2.9.3

CHANGES.md

+23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
# chill #
22

3+
### 0.3.0
4+
* Lots of refactoring around configuration (see chill.config in chill-java)
5+
* Issue #94 KryoPool for pooling Kryo instances and Output buffers
6+
* Issue #85 upgrade to bijection 0.5.2 and scala 2.9.3
7+
* Issue #78 Some, Left, Right serializers in chill(-scala)
8+
* Issue #70 create chill-java for Java only code
9+
* Issue #67 add chill-storm
10+
* Issue #65, #71 import cascading.kryo as chill-hadoop
11+
* Issue #64 fix KryoInjection to be from Any to Array[Byte]
12+
* Issues #61, #63 handle the case of small Map/Set in scala
13+
* Issue #57 use CanBuildFrom in Traversable serialization
14+
15+
Contributors:
16+
17+
94 commits
18+
* P. Oscar Boykin: 46 commits
19+
* Sam Ritchie: 38 commits
20+
* ryanlecompte: 5 commits
21+
* Ngoc Dao: 2 commits
22+
* Dao Ngoc: 1 commits
23+
* Michael Schmitz: 1 commits
24+
* Julien Le Dem: 1 commits
25+
326
### 0.2.3
427
* Update to Kryo 2.21
528
* Update to Bijection 0.4.0

README.md

+104-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,73 @@
11
## Chill [![Build Status](https://secure.travis-ci.org/twitter/chill.png)](http://travis-ci.org/twitter/chill)
22

3-
Scala extensions for the [Kryo serialization library](http://code.google.com/p/kryo/).
3+
Extensions for the [Kryo serialization library](http://code.google.com/p/kryo/) including
4+
serializers and a set of classes to ease configuration of Kryo in systems like Hadoop, Storm,
5+
Akka, etc.
46

5-
Chill provides a a number of Kryo serializers and an Option-like type called the MeatLocker. The MeatLocker allows you to box Kryo-serializable objects and deserialize them lazily on the first call to `get`:
7+
Chill has a set of subprojects: chill-java, chill-hadoop, chill-storm and chill-scala. Other than
8+
chill-scala, all these projects are written in Java so they are easy to use on any JVM platform.
9+
10+
## Chill-Java
11+
12+
The chill-java package includes the `KryoInstantiator` class (factory for Kryo instances)
13+
and the `IKryoRegistrar` interface (adds Serializers to a given Kryo). These two are composable
14+
to build instantiators that create instances of Kryo that have the options and serializers you
15+
need. The benefit of this over a direct Kryo instance is that a Kryo instance is mutable and not
16+
serializable, which limits the safety and reusability of code that works directly with them.
17+
18+
To deserialize or serialize easily, look at `KryoPool`:
19+
20+
```java
21+
int POOL_SIZE = 10;
22+
KryoPool kryo = KryoPool.withByteArrayOutputStream(POOL_SIZE, new KryoInstantiator());
23+
byte[] ser = kryo.toBytesWithClass(myObj);
24+
Object deserObj = kryo.fromBytes(myObj);
25+
```
26+
27+
The KryoPool is a thread-safe way to share Kryo instances and temporary output buffers.
28+
29+
### Chill Config
30+
31+
Hadoop, Storm, and Akka all use a configuration that is basically equivalent to a `Map[String,
32+
String]`. The `com.twitter.chill.config` package makes it easy to build up `KryoInstantiator`
33+
instances given a Config instance, which is an abstract class acting as a thin wrapper over
34+
whatever configuration data the system, such as Hadoop, Storm or Akka, might give.
35+
36+
To configure a KryoInstantiator use `ConfiguredInstantiator` with either reflection,
37+
which takes a class name and instantiates that KryoInstantiator, or an instance of KryoInstantiator
38+
and serializes that instance to use later:
39+
```scala
40+
class TestInst extends KryoInstantiator { override def newKryo = sys.error("blow up") }
41+
42+
// A new Config:
43+
val conf = new JavaMapConfig
44+
// Set-up class-based reflection of our instantiator:
45+
ConfiguredInstantiator.setReflect(conf, classOf[TestInst])
46+
val cci = new ConfiguredInstantiator(conf)
47+
cci.newKryo // uses TestInst
48+
//Or serialize a particular instance into the config to use later (or another node):
49+
50+
ConfiguredInstantiator.setSerialized(conf, new TestInst)
51+
val cci2 = new ConfiguredInstantiator(conf)
52+
cci2.newKryo // uses the particular instance we passed above
53+
```
54+
55+
## Chill in Scala
56+
57+
Scala classes often have a number of properties that distinguish them from usual Java classes. Often
58+
scala classes are immutable, and thus have no zero argument constructor. Secondly, `object` in scala is
59+
a singleton that needs to be carefully serialized. Additionally, scala classes often have synthetic
60+
(compiler generated) fields that need to be serialized, and by default Kryo does not serialize
61+
those.
62+
63+
In addition to a `ScalaKryoInstantiator` which generates Kryo instances with options suitable for
64+
scala, chill provides a number of Kryo serializers for standard scala classes (see below).
65+
66+
### The MeatLocker
67+
68+
Many existing systems use Java serialization. MeatLocker is an object that wraps a given instance
69+
using Kryo serialization internally, but the MeatLocker itself is Java serializable.
70+
The MeatLocker allows you to box Kryo-serializable objects and deserialize them lazily on the first call to `get`:
671

772
```scala
873
import com.twitter.chill.MeatLocker
@@ -16,24 +81,17 @@ box.get == boxedItem.get // true!
1681

1782
To retrieve the boxed item without caching the deserialized value, use `meatlockerInstance.copy`.
1883

19-
To serialize to bytes and deserialize from bytes:
20-
21-
```scala
22-
import com.twitter.chill.KryoInjection
23-
24-
val bytes: Array[Byte] = KryoInjection(someItem)
25-
val option: Option[AnyRef] = KryoInjection.invert(bytes) // None is returned on failure
26-
```
27-
28-
### Handled classes
84+
### Serializers for Scala classes
2985

30-
Chill provides support for singletons, scala Objects and the following types:
86+
These are found in the `chill-scala` directory in the chill jar (originally this project was
87+
only scala serializers). Chill provides support for singletons, scala Objects and the following types:
3188

3289
* Scala primitives
3390
* scala.Enumeration values
3491
* scala.Symbol
3592
* scala.reflect.Manifest
3693
* scala.reflect.ClassManifest
94+
* scala.Function[0-22] closure cleaning (removing unused `$outer` references).
3795
* Collections and sequences
3896
* scala.collection.immutable.Map
3997
* scala.collection.immutable.List
@@ -42,9 +100,41 @@ Chill provides support for singletons, scala Objects and the following types:
42100
* scala.collection.mutable.{Map, Set, Buffer, WrappedArray}
43101
* all 22 scala tuples
44102

103+
## Chill-bijection
104+
[Bijections and Injections](https://github.com/twitter/bijection)
105+
are useful when considering serialization. If you have an Injection from
106+
`T` to `Array[Byte]` you have a serialization. Additionally, if you have a Bijection between `A`
107+
and `B`, and a serialization for `B`, then you have a serialization for `A`. See
108+
`BijectionEnrichedKryo` for easy interop between bijection and chill.
109+
110+
### KryoInjection: easy serialization to byte Arrays
111+
112+
KryoInjection is an injection from `Any` to `Array[Byte]`. To serialize using it:
113+
114+
```scala
115+
import com.twitter.chill.KryoInjection
116+
117+
val bytes: Array[Byte] = KryoInjection(someItem)
118+
val tryDecode: scala.util.Try[Any] = KryoInjection.invert(bytes)
119+
```
120+
121+
KryoInjection can be composed with Bijections and Injections from `com.twitter.bijection`.
122+
45123
## Maven
46124

47-
Current version is `0.2.3`. groupid=`"com.twitter"` artifact=`"chill_2.9.2"` or artifact=`"chill_2.10"`.
125+
Chill modules are available on Maven Central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.3.0`.
126+
127+
Current published artifacts are
128+
129+
* `chill-java`
130+
* `chill-storm`
131+
* `chill-hadoop`
132+
* `chill_2.9.3`
133+
* `chill_2.10`
134+
* `chill-bijection_2.9.3`
135+
* `chill-bijection_2.10`
136+
137+
The suffix denotes the scala version.
48138

49139
## Authors
50140

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 com.twitter.bijection.Injection
20+
import com.twitter.bijection.{ Bufferable, Bijection, ImplicitBijection, Injection }
21+
22+
object BijectionEnrichedKryo {
23+
implicit def enrich(k: Kryo): BijectionEnrichedKryo = new BijectionEnrichedKryo(k)
24+
25+
/** Use a bijection[A,B] then the KSerializer on B
26+
*/
27+
def viaBijection[A,B](kser: KSerializer[B])(implicit bij: ImplicitBijection[A,B], cmf: ClassManifest[B]): KSerializer[A] =
28+
new KSerializer[A] {
29+
def write(k: Kryo, out: Output, obj: A) { kser.write(k, out, bij(obj)) }
30+
def read(k: Kryo, in: Input, cls: Class[A]) =
31+
bij.invert(kser.read(k, in, cmf.erasure.asInstanceOf[Class[B]]))
32+
}
33+
34+
def viaBufferable[T](implicit b: Bufferable[T]): KSerializer[T] =
35+
InjectiveSerializer.asKryo[T](Bufferable.injectionOf[T])
36+
}
37+
38+
class BijectionEnrichedKryo(k: Kryo) {
39+
40+
def injectionForClass[T](implicit inj: Injection[T, Array[Byte]], cmf: ClassManifest[T]): Kryo = {
41+
k.register(cmf.erasure, InjectiveSerializer.asKryo[T])
42+
k
43+
}
44+
45+
def injectionForSubclass[T](implicit inj: Injection[T, Array[Byte]], cmf: ClassManifest[T]): Kryo = {
46+
k.addDefaultSerializer(cmf.erasure, InjectiveSerializer.asKryo[T])
47+
k
48+
}
49+
50+
def bufferableForClass[T](implicit b: Bufferable[T], cmf: ClassManifest[T]): Kryo = {
51+
k.register(cmf.erasure, BijectionEnrichedKryo.viaBufferable[T])
52+
k
53+
}
54+
55+
/** B has to already be registered, then use the KSerializer[B] to create KSerialzer[A]
56+
*/
57+
def forClassViaBijection[A,B](implicit bij: ImplicitBijection[A,B], acmf: ClassManifest[A], bcmf: ClassManifest[B]): Kryo = {
58+
val kserb = k.getSerializer(bcmf.erasure).asInstanceOf[KSerializer[B]]
59+
k.register(acmf.erasure, BijectionEnrichedKryo.viaBijection[A,B](kserb))
60+
k
61+
}
62+
63+
/** Helpful override to alleviate rewriting types. */
64+
def forClassViaBijection[A,B](bij: Bijection[A,B])(implicit acmf: ClassManifest[A], bcmf: ClassManifest[B]): Kryo = {
65+
implicit def implicitBij = bij
66+
this.forClassViaBijection[A, B]
67+
}
68+
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.twitter.chill
2+
3+
import com.twitter.bijection.Injection
4+
5+
// need the root, because java looks like chill.java to scalac. :(
6+
import _root_.java.io.Serializable
7+
8+
/**
9+
* @author Sam Ritchie
10+
* @author Oscar Boykin
11+
*
12+
* Convenience class that holds both a Class[T] and an Injection from
13+
* type T to Array[Byte].
14+
*/
15+
16+
object InjectionRegistrar {
17+
def apply[T](klass: Class[T], injection: Injection[T, Array[Byte]]): InjectionRegistrar[T] =
18+
new InjectionRegistrar(klass, injection)
19+
}
20+
21+
class InjectionRegistrar[T](val klass: Class[T], @transient b: Injection[T, Array[Byte]])
22+
extends IKryoRegistrar with Serializable {
23+
protected val bBox = MeatLocker(b)
24+
25+
implicit def injection = bBox.copy
26+
27+
def apply(k: Kryo) {
28+
if(!k.alreadyRegistered(klass)) {
29+
k.register(klass, InjectiveSerializer.asKryo[T])
30+
}
31+
}
32+
}
33+
34+
object InjectionDefaultRegistrar {
35+
def apply[T](klass: Class[T], injection: Injection[T, Array[Byte]]): InjectionDefaultRegistrar[T] =
36+
new InjectionDefaultRegistrar(klass, injection)
37+
}
38+
39+
class InjectionDefaultRegistrar[T](klass: Class[T], @transient b: Injection[T, Array[Byte]])
40+
extends InjectionRegistrar(klass, b) {
41+
override def apply(k: Kryo) {
42+
if(!k.alreadyRegistered(klass)) {
43+
k.addDefaultSerializer(klass, InjectiveSerializer.asKryo[T])
44+
k.register(klass)
45+
}
46+
}
47+
}
48+

chill-scala/src/main/scala/com/twitter/chill/InjectiveSerializer.scala chill-bijection/src/main/scala/com/twitter/chill/InjectiveSerializer.scala

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.twitter.chill
22

3-
import com.esotericsoftware.kryo.{ Kryo, Serializer => KSerializer }
4-
import com.esotericsoftware.kryo.io.{ Input, Output }
53
import com.twitter.bijection.Injection
64

75
/**
@@ -29,11 +27,6 @@ class InjectiveSerializer[T] private (injection: Injection[T, Array[Byte]]) exte
2927
def read(kser: Kryo, in: Input, cls: Class[T]): T = {
3028
val bytes = new Array[Byte](in.readInt(true))
3129
in.readBytes(bytes)
32-
injection.invert(bytes) match {
33-
case Some(t) => t
34-
case None => throw new RuntimeException(
35-
"Could not deserialize instance of " + cls.getName
36-
)
37-
}
30+
injection.invert(bytes).get
3831
}
3932
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 com.twitter.bijection.{ Bijection, Injection, Inversion }
20+
21+
import _root_.java.io.Serializable
22+
23+
/**
24+
* A default KryoInjection that uses the ScalaKryoInstantiator with
25+
* ByteArrayOutputStream as the backing buffer
26+
*/
27+
object KryoInjection extends Injection[Any, Array[Byte]] {
28+
def apply(obj: Any): Array[Byte] = ScalaKryoInstantiator.defaultPool.toBytesWithClass(obj)
29+
def invert(b: Array[Byte]) = Inversion.attempt(b) {
30+
ScalaKryoInstantiator.defaultPool.fromBytes(_)
31+
}
32+
33+
/**
34+
* Create a new KryoInjection instance that serializes items using
35+
* the supplied KryoPool instance. For this to be serializable, you
36+
* need the call by name semantics
37+
*/
38+
def instance(kryoPool: => KryoPool): Injection[Any, Array[Byte]] =
39+
new KryoInjectionInstance(kryoPool)
40+
41+
/** Creates a small pool (size 2) and uses it as an Injection
42+
* Note the implicit in the package from () => Kryo to KryoInstatiator.
43+
* It is ESSENTIAL that this function is allocating new Kryos, or we will
44+
* not be thread-safe
45+
*/
46+
def instance(ki: KryoInstantiator, poolSize: Int = 2): Injection[Any, Array[Byte]] =
47+
new KryoInjectionInstance(KryoPool.withByteArrayOutputStream(poolSize, ki))
48+
}
49+
50+
/**
51+
* Use this if you want to control the KryoPool.
52+
* This is thread-safe since the KryoPool is
53+
*/
54+
class KryoInjectionInstance(lazyKryoP: => KryoPool) extends Injection[Any, Array[Byte]] {
55+
private val mutex = new AnyRef with Serializable // some serializable object
56+
@transient private var kpool: KryoPool = null
57+
58+
private def kryoP: KryoPool = mutex.synchronized {
59+
if(null == kpool) { kpool = lazyKryoP }
60+
kpool
61+
}
62+
63+
def apply(obj: Any): Array[Byte] = kryoP.toBytesWithClass(obj)
64+
def invert(b: Array[Byte]) = Inversion.attempt(b) { kryoP.fromBytes(_) }
65+
}

0 commit comments

Comments
 (0)