Skip to content

Commit

Permalink
Merge pull request #228 from sriramkrishnan/develop
Browse files Browse the repository at this point in the history
Getting rid of a circular dependency between bijection-macros and chill
  • Loading branch information
johnynek committed Sep 3, 2015
2 parents 565dc29 + 747808d commit ef6b049
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import org.scalatest.prop.PropertyChecks

import com.twitter.bijection._
import com.twitter.bijection.macros._
import com.twitter.chill.Externalizer

trait MacroPropTests extends PropSpec with PropertyChecks with Matchers with MacroTestHelper {
import MacroImplicits._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import org.scalatest.{ Matchers, WordSpec }

import com.twitter.bijection._
import com.twitter.bijection.macros._
import com.twitter.chill.Externalizer

import scala.util.{ Failure, Success }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package com.twitter.bijection.macros

import org.scalatest.Matchers
import com.twitter.chill.Externalizer

import _root_.java.io.{
ByteArrayOutputStream,
ByteArrayInputStream,
Externalizable,
ObjectInput,
ObjectOutput,
ObjectInputStream,
ObjectOutputStream
}

import _root_.java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }

object MacroCaseClasses extends java.io.Serializable {
type Atup = (Int, String)
Expand All @@ -20,6 +31,98 @@ object MacroCaseClasses extends java.io.Serializable {
class SampleClassD // Non-case class
}

object Externalizer {
def apply[T](t: T): Externalizer[T] = {
val x = new Externalizer[T]
x.set(t)
x
}
}

/**
* This is a simplified version of com.twitter.chill.Externalizer
* which only does Java serialization
*/
class Externalizer[T] extends Externalizable {
// Either points to a result or a delegate Externalizer to fufil that result.
private var item: Either[Externalizer[T], Option[T]] = Right(None)
import Externalizer._

@transient private val doesJavaWork = new AtomicReference[Option[Boolean]](None)
@transient private val testing = new AtomicBoolean(false)

// No vals or var's below this line!

def getOption: Option[T] = item match {
case Left(e) => e.getOption
case Right(i) => i
}

def get: T = getOption.get // This should never be None when get is called

/**
* Unfortunately, Java serialization requires mutable objects if
* you are going to control how the serialization is done.
* Use the companion object to creat new instances of this
*/
def set(it: T): Unit = {
item match {
case Left(e) => e.set(it)
case Right(x) =>
assert(x.isEmpty, "Tried to call .set on an already constructed Externalizer")
item = Right(Some(it))
}
}

// 1 here is 1 thread, since we will likely only serialize once
// this should not be a val because we don't want to capture a reference

def javaWorks: Boolean =
doesJavaWork.get match {
case Some(v) => v
case None => probeJavaWorks
}

/**
* Try to round-trip and see if it works without error
*/
private def probeJavaWorks: Boolean = {
if (!testing.compareAndSet(false, true)) return true
try {
val baos = new ByteArrayOutputStream()
val oos = new ObjectOutputStream(baos)
oos.writeObject(getOption)
val bytes = baos.toByteArray
val testInput = new ByteArrayInputStream(bytes)
val ois = new ObjectInputStream(testInput)
ois.readObject // this may throw
doesJavaWork.set(Some(true))
true
} catch {
case t: Throwable =>
t.printStackTrace
doesJavaWork.set(Some(false))
false
} finally {
testing.set(false)
}
}

override def readExternal(in: ObjectInput) = readJava(in)

private def readJava(in: ObjectInput) {
item = Right(in.readObject.asInstanceOf[Option[T]])
}

protected def writeJava(out: ObjectOutput): Boolean =
javaWorks && {
out.writeObject(getOption)
true
}

override def writeExternal(out: ObjectOutput) = writeJava(out)
}

trait MacroTestHelper extends Matchers {
def canExternalize(t: AnyRef) { Externalizer(t).javaWorks shouldBe true }
}
3 changes: 1 addition & 2 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,7 @@ object BijectionBuild extends Build {
libraryDependencies <++= (scalaVersion) { scalaVersion => Seq(
"org.scala-lang" % "scala-library" % scalaVersion,
"org.scala-lang" % "scala-reflect" % scalaVersion,
"org.scalatest" %% "scalatest" % "2.2.2" % "test",
"com.twitter" %% "chill" % "0.5.0" % "test"
"org.scalatest" %% "scalatest" % "2.2.2" % "test"
) ++ (if (scalaVersion.startsWith("2.10")) Seq("org.scalamacros" %% "quasiquotes" % "2.0.1") else Seq())
},
addCompilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
Expand Down

0 comments on commit ef6b049

Please sign in to comment.