Permalink
Browse files

[reactivemongo] Manage connection handler per actor, so that an singl…

…e ActorSystem can manage several handlers. Documentation about driver manager with SBT or Specs2.
  • Loading branch information...
cchantep
cchantep committed Oct 29, 2014
1 parent b7b9c7f commit bd6763c3e1d513d05cfefbb887e20a672b1c71db
@@ -4,36 +4,14 @@ import Keys._
trait ReactiveMongo { deps: Dependencies
def scalacPlugin: Project
val reactiveMongoVer = settingKey[String]("Reactive Mongo version")
// Shared dependency
val reactiveMongoLib = "org.reactivemongo" %% "reactivemongo"
val reactiveResolvers = Seq(
"Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/releases/",
"Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/")
private lazy val reactiveMongoGen =
Project(id = "reactive-mongo-sbt",
base = file("project") / "reactive-mongo-sbt").settings(
name := "reactive-mongo-sbt",
javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
autoScalaLibrary := false,
scalacOptions += "-feature",
resolvers ++= reactiveResolvers,
reactiveMongoVer in ThisBuild := {
if (scalaVersion.value startsWith "2.11") "0.10.5.0.akka23"
else "0.10.0"
},
libraryDependencies ++= Seq(
reactiveMongoLib % reactiveMongoVer.value,
"org.javassist" % "javassist" % "3.18.2-GA")
)
"Sonatype Snapshots".at(
"https://oss.sonatype.org/content/repositories/snapshots/"))
lazy val generatedClassDirectory = settingKey[File](
"Directory where classes get generated")
lazy val generatedClasses = taskKey[Seq[(File, String)]]("Generated classes")
lazy val reactiveMongo =
Project(id = "reactive-mongo", base = file("reactive-mongo")).settings(
name := "reactive-mongo",
@@ -56,45 +34,13 @@ trait ReactiveMongo { deps: Dependencies ⇒
},
resolvers ++= reactiveResolvers,
libraryDependencies ++= Seq(
reactiveMongoLib % reactiveMongoVer.value,
"org.reactivemongo" %% "reactivemongo" % "0.10.5.0.akka23",
"com.jsuereth" %% "scala-arm" % "1.4",
"com.chuusai" % "shapeless" % "2.0.0" % Test cross CrossVersion.
binaryMapped {
case "2.10" => scalaVersion.value
case x => x
},
specs2Test),
generatedClassDirectory := {
val dir = target.value / "generated_classes"
if (!dir.exists) dir.mkdirs()
dir
},
generatedClasses <<= Def.task {
val cp = (fullClasspath in (reactiveMongoGen, Compile)).value
val cl = classpath.ClasspathUtilities.toLoader(cp.files)
val genClass = cl loadClass "acolyte.reactivemongo.ActorSystemGenerator"
val generator = genClass.newInstance.
asInstanceOf[{def writeTo(out: File): Array[File] }]
val outdir = generatedClassDirectory.value
val generated: Array[File] = generator writeTo outdir
generated.foldLeft(Seq[(File, String)]()) { (s, f) =>
val path = f.getAbsolutePath
s :+ (f -> path.drop(outdir.getAbsolutePath.length+1))
}
} dependsOn(compile in (reactiveMongoGen, Compile)),
managedClasspath in Compile := {
val cp = (managedClasspath in Compile).value
cp :+ Attributed.blank(generatedClassDirectory.value)
},
managedClasspath in Test := { // Same for test
val cp = (managedClasspath in Test).value
cp :+ Attributed.blank(generatedClassDirectory.value)
},
compile in Compile <<= (compile in Compile) dependsOn generatedClasses,
mappings in (Compile, packageBin) := {
val ms = mappings.in(Compile, packageBin).value
ms ++ generatedClasses.value // add generated classes to package
}
).dependsOn(/*reactiveMongoGen, */scalacPlugin)
specs2Test)
).dependsOn(scalacPlugin)
}
@@ -1,29 +0,0 @@
package acolyte.reactivemongo;
import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import akka.actor.Props;
/**
* Reference factory with underlying actor system provided.
*/
public interface ActorRefFactory {
/**
* Returns actor reference according given properties.
*
* @param system Actor system
* @param props Actor properties
*/
public ActorRef actorOf(ActorSystem system, Props props);
/**
* Returns actor reference according given properties and name.
*
* @param system Actor system
* @param props Actor properties
* @param name Actor name
*/
public ActorRef actorOf(ActorSystem system, Props props, String name);
} // end of class ActorRefFactory
@@ -1,175 +0,0 @@
package acolyte.reactivemongo;
import java.io.File;
import javassist.CtNewConstructor;
import javassist.LoaderClassPath;
import javassist.CtNewMethod;
import javassist.ClassPool;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.CtField;
import javassist.CtClass;
import akka.actor.ActorSystem;
/**
* Actor system generator.
*/
public final class ActorSystemGenerator {
public static File[] writeTo(File outdir) throws Exception {
final ClassPool pool = ClassPool.getDefault();
final LoaderClassPath lcp =
new LoaderClassPath(ActorSystem.class.getClassLoader());
pool.appendClassPath(lcp);
final CtClass asc = pool.get(ActorSystem.class.getName());
final CtClass fc = pool.get(ActorRefFactory.class.getName());
final CtClass pc =
pool.makeClass("acolyte.reactivemongo.ActorSystem");
pc.setSuperclass(asc);
// New fields
pc.addField(CtField.make("private final " + ActorSystem.class.getName() + " underlying;", pc));
pc.addField(CtField.make("private final " + ActorRefFactory.class.getName() + " refFactory;", pc));
// New constructor
pc.addConstructor(CtNewConstructor.make(new CtClass[] { asc, fc }, new CtClass[0], "{ if ($1 == null) { throw new IllegalArgumentException(\"Missing underlying system\"); } if ($2 == null) { throw new IllegalArgumentException(\"Missing reference factory\"); } this.underlying = $1; this.refFactory = $2; }", pc));
final CtMethod[] ms = pc.getMethods();
for (int i = 0; i < ms.length; i++) {
if (!ms[i].getDeclaringClass().
getPackageName().startsWith("akka.")) {
continue;
} // end of if
// ---
final int mod = Modifier.
clear(ms[i].getModifiers(), Modifier.ABSTRACT);
if (Modifier.isStatic(mod) || Modifier.isPrivate(mod) ||
Modifier.isNative(mod) || Modifier.isFinal(mod)) {
continue;
} // end of if
// ---
final CtClass returnType = ms[i].getReturnType();
final CtClass[] paramTypes = ms[i].getParameterTypes();
final String name = ms[i].getName();
final String body = (returnType == CtClass.voidType)
? "{ underlying." + name + "($$); }"
: ("actorOf".equals(name) &&
(paramTypes.length == 1 || paramTypes.length == 2))
? "{ return this.refFactory.actorOf(underlying, $$); }"
: "{ return underlying." + name + "($$); }";
pc.addMethod(CtNewMethod.make(mod,
ms[i].getReturnType(),
ms[i].getName(),
paramTypes,
ms[i].getExceptionTypes(),
body,
pc));
} // end of for
final File packageDir =
new File(new File(outdir, "acolyte"), "reactivemongo");
packageDir.mkdirs();
final String outpath = outdir.getAbsolutePath();
fc.writeFile(outpath);
pc.writeFile(outpath);
return new File[] {
new File(packageDir, fc.getSimpleName() + ".class"),
new File(packageDir, pc.getSimpleName() + ".class")
};
} // end of writeTo
// Used to proxy ReactiveMongo actor system and do some reverse engineering
public static File[] writeReverseEngineering(File outdir) throws Exception {
final ClassPool pool = ClassPool.getDefault();
final LoaderClassPath lcp =
new LoaderClassPath(ActorSystem.class.getClassLoader());
pool.appendClassPath(lcp);
final CtClass asc = pool.get(ActorSystem.class.getName());
final CtClass fc = pool.get(ReverseEngineeringRefFactory.class.getName());
final CtClass pc =
pool.makeClass("acolyte.reactivemongo.ActorSystem");
pc.setSuperclass(asc);
// New fields
pc.addField(CtField.make("private final " + ActorSystem.class.getName() + " underlying;", pc));
pc.addField(CtField.make("private final " + ReverseEngineeringRefFactory.class.getName() + " refFactory;", pc));
// New constructor
pc.addConstructor(CtNewConstructor.make(new CtClass[] { asc, fc }, new CtClass[0], "{ if ($1 == null) { throw new IllegalArgumentException(\"Missing underlying system\"); } if ($2 == null) { throw new IllegalArgumentException(\"Missing reference factory\"); } this.underlying = $1; this.refFactory = $2; }", pc));
final CtMethod[] ms = pc.getMethods();
for (int i = 0; i < ms.length; i++) {
if (!ms[i].getDeclaringClass().
getPackageName().startsWith("akka.")) {
continue;
} // end of if
// ---
final int mod = Modifier.
clear(ms[i].getModifiers(), Modifier.ABSTRACT);
if (Modifier.isStatic(mod) || Modifier.isPrivate(mod) ||
Modifier.isNative(mod) || Modifier.isFinal(mod)) {
continue;
} // end of if
// ---
final CtClass returnType = ms[i].getReturnType();
final String name = ms[i].getName();
final String body = (returnType == CtClass.voidType)
? "{ underlying." + name + "($$); }"
: !"akka.actor.ActorRef".equals(returnType.getName())
? "{ return underlying." + name + "($$); }"
: "{ final akka.actor.ActorRef then = underlying." + name +
"($$); return this.refFactory.before(underlying, then); }";
pc.addMethod(CtNewMethod.make(mod,
ms[i].getReturnType(),
ms[i].getName(),
ms[i].getParameterTypes(),
ms[i].getExceptionTypes(),
body,
pc));
} // end of for
final File packageDir =
new File(new File(outdir, "acolyte"), "reactivemongo");
packageDir.mkdirs();
final String outpath = outdir.getAbsolutePath();
fc.writeFile(outpath);
pc.writeFile(outpath);
return new File[] {
new File(packageDir, fc.getSimpleName() + ".class"),
new File(packageDir, pc.getSimpleName() + ".class")
};
} // end of writeReverseEngineering
} // end of class ActorSystemGenerator
@@ -1,19 +0,0 @@
package acolyte.reactivemongo;
import akka.actor.ActorSystem;
import akka.actor.ActorRef;
/**
* Reference factory for reverse engineering of Mongo actors.
*/
public interface ReverseEngineeringRefFactory {
/**
* Creates an ActorRef 'before' another.
*
* @param system Actor system
* @param then Actor to which message should be normally forwarded
*/
public ActorRef before(ActorSystem system, ActorRef then);
} // end of class ReverseEngineeringRefFactory
Oops, something went wrong.

0 comments on commit bd6763c

Please sign in to comment.