From 4dd5e9612bcffdbbea9b23ccf604262385f06c35 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 Jan 2012 15:45:12 +0100 Subject: [PATCH] Migration kit and start of migration documentation. See #1406. * Documentation of migration kit * Documentation of some of the changes * akka-actor-migration module containing GlobalActorSystem, OldXxx classes and some implicit conversions * Tried migration of WebWords sample * Tried migration of akka-samples/async-workers * Tried migration of akka-samples-trading --- .../scala/akka/actor/GlobalActorSystem.scala | 64 ++++ .../src/main/scala/akka/actor/OldActor.scala | 171 +++++++++ .../main/scala/akka/actor/OldScheduler.scala | 75 ++++ .../main/scala/akka/config/OldConfig.scala | 162 ++++++++ .../main/scala/akka/dispatch/OldFuture.scala | 65 ++++ .../scala/akka/event/OldEventHandler.scala | 81 ++++ .../main/scala/akka/migration/package.scala | 34 ++ .../src/main/scala/akka/routing/Routing.scala | 80 ++-- akka-docs/general/jmm.rst | 2 + .../project/migration-guide-1.3.x-2.0.x.rst | 350 ++++++++++++++++++ .../code/akka/docs/actor/ActorDocSpec.scala | 3 + project/AkkaBuild.scala | 9 +- 12 files changed, 1055 insertions(+), 41 deletions(-) create mode 100644 akka-actor-migration/src/main/scala/akka/actor/GlobalActorSystem.scala create mode 100644 akka-actor-migration/src/main/scala/akka/actor/OldActor.scala create mode 100644 akka-actor-migration/src/main/scala/akka/actor/OldScheduler.scala create mode 100644 akka-actor-migration/src/main/scala/akka/config/OldConfig.scala create mode 100644 akka-actor-migration/src/main/scala/akka/dispatch/OldFuture.scala create mode 100644 akka-actor-migration/src/main/scala/akka/event/OldEventHandler.scala create mode 100644 akka-actor-migration/src/main/scala/akka/migration/package.scala diff --git a/akka-actor-migration/src/main/scala/akka/actor/GlobalActorSystem.scala b/akka-actor-migration/src/main/scala/akka/actor/GlobalActorSystem.scala new file mode 100644 index 0000000000..e0ba90b1f9 --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/actor/GlobalActorSystem.scala @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.actor + +import java.io.File + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import com.typesafe.config.ConfigParseOptions +import com.typesafe.config.ConfigResolveOptions + +@deprecated("use ActorSystem instead", "2.0") +object GlobalActorSystem extends ActorSystemImpl("GlobalSystem", OldConfigurationLoader.defaultConfig) { + start() +} + +/** + * Loads configuration (akka.conf) from same location as Akka 1.x + */ +@deprecated("use default config location or write your own configuration loader", "2.0") +object OldConfigurationLoader { + + val defaultConfig: Config = { + val cfg = fromProperties orElse fromClasspath orElse fromHome getOrElse emptyConfig + val config = cfg.withFallback(ConfigFactory.defaultReference) + config.checkValid(ConfigFactory.defaultReference, "akka") + config + } + + // file extensions (.conf, .json, .properties), are handled by parseFileAnySyntax + val defaultLocation: String = (systemMode orElse envMode).map("akka." + _).getOrElse("akka") + + private def envMode = System.getenv("AKKA_MODE") match { + case null | "" ⇒ None + case value ⇒ Some(value) + } + + private def systemMode = System.getProperty("akka.mode") match { + case null | "" ⇒ None + case value ⇒ Some(value) + } + + private def configParseOptions = ConfigParseOptions.defaults.setAllowMissing(false) + + private def fromProperties = try { + val property = Option(System.getProperty("akka.config")) + property.map(p ⇒ + ConfigFactory.systemProperties.withFallback( + ConfigFactory.parseFileAnySyntax(new File(p), configParseOptions))) + } catch { case _ ⇒ None } + + private def fromClasspath = try { + Option(ConfigFactory.systemProperties.withFallback( + ConfigFactory.parseResourcesAnySyntax(ActorSystem.getClass, "/" + defaultLocation, configParseOptions))) + } catch { case _ ⇒ None } + + private def fromHome = try { + Option(ConfigFactory.systemProperties.withFallback( + ConfigFactory.parseFileAnySyntax(new File(ActorSystem.GlobalHome.get + "/config/" + defaultLocation), configParseOptions))) + } catch { case _ ⇒ None } + + private def emptyConfig = ConfigFactory.systemProperties +} \ No newline at end of file diff --git a/akka-actor-migration/src/main/scala/akka/actor/OldActor.scala b/akka-actor-migration/src/main/scala/akka/actor/OldActor.scala new file mode 100644 index 0000000000..0a9238209e --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/actor/OldActor.scala @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.actor + +import akka.japi.Creator +import akka.util.Timeout +import akka.dispatch.Future +import akka.dispatch.OldFuture +import akka.util.Duration +import java.util.concurrent.TimeUnit +import java.net.InetSocketAddress + +/** + * Migration replacement for `object akka.actor.Actor`. + */ +@deprecated("use ActorRefFactory (ActorSystem or ActorContext) to create actors", "2.0") +object OldActor { + + /** + * Creates an ActorRef out of the Actor with type T. + * It will be automatically started, i.e. remove old call to `start()`. + * + */ + @deprecated("use ActorRefFactory (ActorSystem or ActorContext) to create actors", "2.0") + def actorOf[T <: Actor: Manifest]: ActorRef = actorOf(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]]) + + /** + * Creates an ActorRef out of the Actor of the specified Class. + * It will be automatically started, i.e. remove old call to `start()`. + */ + @deprecated("use ActorRefFactory (ActorSystem or ActorContext) to create actors", "2.0") + def actorOf(clazz: Class[_ <: Actor]): ActorRef = GlobalActorSystem.actorOf(Props(clazz)) + + /** + * Creates an ActorRef out of the Actor. Allows you to pass in a factory function + * that creates the Actor. Please note that this function can be invoked multiple + * times if for example the Actor is supervised and needs to be restarted. + * + * It will be automatically started, i.e. remove old call to `start()`. + */ + @deprecated("use ActorRefFactory (ActorSystem or ActorContext) to create actors", "2.0") + def actorOf(factory: ⇒ Actor): ActorRef = GlobalActorSystem.actorOf(Props(factory)) + + /** + * Creates an ActorRef out of the Actor. Allows you to pass in a factory (Creator) + * that creates the Actor. Please note that this function can be invoked multiple + * times if for example the Actor is supervised and needs to be restarted. + *

+ * JAVA API + */ + @deprecated("use ActorRefFactory (ActorSystem or ActorContext) to create actors", "2.0") + def actorOf(creator: Creator[Actor]): ActorRef = GlobalActorSystem.actorOf(Props(creator)) + + @deprecated("OldActor.remote should not be used", "2.0") + lazy val remote: OldRemoteSupport = new OldRemoteSupport + +} + +@deprecated("use Actor", "2.0") +abstract class OldActor extends Actor { + + implicit def askTimeout: Timeout = akka.migration.askTimeout + + implicit def future2OldFuture[T](future: Future[T]): OldFuture[T] = akka.migration.future2OldFuture(future) + + implicit def actorRef2OldActorRef(actorRef: ActorRef) = new OldActorRef(actorRef) + + @deprecated("Use context.become instead", "2.0") + def become(behavior: Receive, discardOld: Boolean = true) = context.become(behavior, discardOld) + + @deprecated("Use context.unbecome instead", "2.0") + def unbecome() = context.unbecome() + + class OldActorRef(actorRef: ActorRef) { + @deprecated("Actors are automatically started when creatd, i.e. remove old call to start()", "2.0") + def start(): ActorRef = actorRef + + @deprecated("Stop with ActorSystem or ActorContext instead", "2.0") + def exit() = stop() + + @deprecated("Stop with ActorSystem or ActorContext instead", "2.0") + def stop(): Unit = context.stop(actorRef) + + @deprecated("Use context.getReceiveTimeout instead", "2.0") + def getReceiveTimeout(): Option[Long] = context.receiveTimeout.map(_.toMillis) + + @deprecated("Use context.setReceiveTimeout instead", "2.0") + def setReceiveTimeout(timeout: Long) = context.setReceiveTimeout(Duration(timeout, TimeUnit.MILLISECONDS)) + + @deprecated("Use context.getReceiveTimeout instead", "2.0") + def receiveTimeout: Option[Long] = getReceiveTimeout() + + @deprecated("Use context.setReceiveTimeout instead", "2.0") + def receiveTimeout_=(timeout: Option[Long]) = setReceiveTimeout(timeout.getOrElse(0L)) + + @deprecated("Use self.isTerminated instead", "2.0") + def isShutdown: Boolean = self.isTerminated + + @deprecated("Use sender instead", "2.0") + def channel() = context.sender + + @deprecated("Use sender instead", "2.0") + def sender() = Some(context.sender) + + @deprecated("Use sender ! instead", "2.0") + def reply(message: Any) = context.sender.!(message, context.self) + + @deprecated("Use sender ! instead", "2.0") + def tryReply(message: Any): Boolean = { + reply(message) + true + } + + @deprecated("Use sender ! instead", "2.0") + def tryTell(message: Any)(implicit sender: ActorRef = context.self): Boolean = { + actorRef.!(message)(sender) + true + } + + @deprecated("Use sender ! akka.actor.Status.Failure(e) instead", "2.0") + def sendException(ex: Throwable): Boolean = { + context.sender.!(akka.actor.Status.Failure(ex), context.self) + true + } + } +} + +class OldRemoteSupport { + + @deprecated("remote.start is not needed", "2.0") + def start() {} + + @deprecated("remote.start is not needed, use configuration to specify RemoteActorRefProvider, host and port", "2.0") + def start(host: String, port: Int) {} + + @deprecated("remote.start is not needed, use configuration to specify RemoteActorRefProvider, host and port", "2.0") + def start(host: String, port: Int, loader: ClassLoader) {} + + @deprecated("remote.shutdown is not needed", "2.0") + def shutdown() {} + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(classNameOrServiceId: String, hostname: String, port: Int): ActorRef = + GlobalActorSystem.actorFor("akka://%s@%s:%s/user/%s".format(GlobalActorSystem.name, hostname, port, classNameOrServiceId)) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(classNameOrServiceId: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(classNameOrServiceId, hostname, port) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(serviceId: String, className: String, hostname: String, port: Int): ActorRef = + actorFor(serviceId, hostname, port) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(serviceId: String, className: String, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(serviceId, hostname, port) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(classNameOrServiceId: String, timeout: Long, hostname: String, port: Int): ActorRef = + actorFor(classNameOrServiceId, hostname, port) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(classNameOrServiceId: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): ActorRef = + actorFor(classNameOrServiceId, hostname, port) + + @deprecated("use actorFor in ActorRefProvider (ActorSystem or ActorContext) instead", "2.0") + def actorFor(serviceId: String, className: String, timeout: Long, hostname: String, port: Int): ActorRef = + actorFor(serviceId, hostname, port) + +} \ No newline at end of file diff --git a/akka-actor-migration/src/main/scala/akka/actor/OldScheduler.scala b/akka-actor-migration/src/main/scala/akka/actor/OldScheduler.scala new file mode 100644 index 0000000000..7b487bf5db --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/actor/OldScheduler.scala @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.actor + +import java.util.concurrent.TimeUnit +import akka.util.Duration + +/** + * Migration replacement for `object akka.actor.Scheduler`. + */ +@deprecated("use ActorSystem.scheduler instead", "2.0") +object OldScheduler { + + /** + * Schedules to send the specified message to the receiver after initialDelay and then repeated after delay + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def schedule(receiver: ActorRef, message: Any, initialDelay: Long, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.schedule( + Duration(initialDelay, timeUnit), + Duration(delay, timeUnit), + receiver, + message) + + /** + * Schedules to run specified function to the receiver after initialDelay and then repeated after delay + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def schedule(f: () ⇒ Unit, initialDelay: Long, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.schedule( + Duration(initialDelay, timeUnit), + Duration(delay, timeUnit), + new Runnable { def run = f() }) + + /** + * Schedules to run specified runnable to the receiver after initialDelay and then repeated after delay. + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def schedule(runnable: Runnable, initialDelay: Long, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.schedule( + Duration(initialDelay, timeUnit), + Duration(delay, timeUnit), + runnable) + + /** + * Schedules to send the specified message to the receiver after delay + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def scheduleOnce(receiver: ActorRef, message: Any, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.scheduleOnce( + Duration(delay, timeUnit), + receiver, + message) + + /** + * Schedules a function to be run after delay. + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def scheduleOnce(f: () ⇒ Unit, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.scheduleOnce( + Duration(delay, timeUnit), + new Runnable { def run = f() }) + + /** + * Schedules a runnable to be run after delay, + */ + @deprecated("use ActorSystem.scheduler instead", "2.0") + def scheduleOnce(runnable: Runnable, delay: Long, timeUnit: TimeUnit): Cancellable = + GlobalActorSystem.scheduler.scheduleOnce( + Duration(delay, timeUnit), + runnable) + +} + diff --git a/akka-actor-migration/src/main/scala/akka/config/OldConfig.scala b/akka-actor-migration/src/main/scala/akka/config/OldConfig.scala new file mode 100644 index 0000000000..69d7e15d41 --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/config/OldConfig.scala @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.config +import akka.actor.GlobalActorSystem +import com.typesafe.config.Config + +/** + * Migration replacement for `object akka.config.Config`. + */ +@deprecated("use ActorSystem.settings.config instead", "2.0") +object OldConfig { + + val config = new OldConfiguration(GlobalActorSystem.settings.config) + +} + +/** + * Migration adapter for `akka.config.Configuration` + */ +@deprecated("use ActorSystem.settings.config (com.typesafe.config.Config) instead", "2.0") +class OldConfiguration(config: Config) { + + import scala.collection.JavaConverters._ + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def contains(key: String): Boolean = config.hasPath(key) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def keys: Iterable[String] = config.root.keySet.asScala + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getAny(key: String): Option[Any] = { + try { + Option(config.getAnyRef(key)) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getAny(key: String, defaultValue: Any): Any = getAny(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getSeqAny(key: String): Seq[Any] = { + try { + config.getAnyRefList(key).asScala + } catch { + case _ ⇒ Seq.empty[Any] + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getString(key: String): Option[String] = + try { + Option(config.getString(key)) + } catch { + case _ ⇒ None + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getString(key: String, defaultValue: String): String = getString(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getList(key: String): Seq[String] = { + try { + config.getStringList(key).asScala + } catch { + case _ ⇒ Seq.empty[String] + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getInt(key: String): Option[Int] = { + try { + Option(config.getInt(key)) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getInt(key: String, defaultValue: Int): Int = getInt(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getLong(key: String): Option[Long] = { + try { + Option(config.getLong(key)) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getLong(key: String, defaultValue: Long): Long = getLong(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getFloat(key: String): Option[Float] = { + try { + Option(config.getDouble(key).toFloat) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getFloat(key: String, defaultValue: Float): Float = getFloat(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getDouble(key: String): Option[Double] = { + try { + Option(config.getDouble(key)) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getDouble(key: String, defaultValue: Double): Double = getDouble(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getBoolean(key: String): Option[Boolean] = { + try { + Option(config.getBoolean(key)) + } catch { + case _ ⇒ None + } + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getBoolean(key: String, defaultValue: Boolean): Boolean = getBoolean(key).getOrElse(defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getBool(key: String): Option[Boolean] = getBoolean(key) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getBool(key: String, defaultValue: Boolean): Boolean = getBoolean(key, defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def apply(key: String): String = getString(key) match { + case None ⇒ throw new ConfigurationException("undefined config: " + key) + case Some(v) ⇒ v + } + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def apply(key: String, defaultValue: String) = getString(key, defaultValue) + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def apply(key: String, defaultValue: Int) = getInt(key, defaultValue) + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def apply(key: String, defaultValue: Long) = getLong(key, defaultValue) + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def apply(key: String, defaultValue: Boolean) = getBool(key, defaultValue) + + @deprecated("use new com.typesafe.config.Config API instead", "2.0") + def getSection(name: String): Option[OldConfiguration] = { + try { + Option(new OldConfiguration(config.getConfig(name))) + } catch { + case _ ⇒ None + } + } +} \ No newline at end of file diff --git a/akka-actor-migration/src/main/scala/akka/dispatch/OldFuture.scala b/akka-actor-migration/src/main/scala/akka/dispatch/OldFuture.scala new file mode 100644 index 0000000000..f53a3dd11b --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/dispatch/OldFuture.scala @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.dispatch + +import java.util.concurrent.TimeoutException +import akka.util.duration._ +import akka.AkkaException +import akka.util.BoxedType +import akka.util.Duration +import akka.actor.GlobalActorSystem + +/** + * Some old methods made available through implicit conversion in + * [[akka.migration]]. + */ +@deprecated("use new Future api instead", "2.0") +class OldFuture[T](future: Future[T]) { + + @deprecated("use akka.dispatch.Await.result instead", "2.0") + def get: T = try { + Await.result(future, GlobalActorSystem.settings.ActorTimeout.duration) + } catch { + case e: TimeoutException ⇒ throw new FutureTimeoutException(e.getMessage, e) + } + + @deprecated("use akka.dispatch.Await.ready instead", "2.0") + def await: Future[T] = await(GlobalActorSystem.settings.ActorTimeout.duration) + + @deprecated("use akka.dispatch.Await.ready instead", "2.0") + def await(atMost: Duration) = try { + Await.ready(future, atMost) + future + } catch { + case e: TimeoutException ⇒ throw new FutureTimeoutException(e.getMessage, e) + } + + @deprecated("use new Future api instead", "2.0") + def as[A](implicit m: Manifest[A]): Option[A] = { + try await catch { case _: FutureTimeoutException ⇒ } + future.value match { + case None ⇒ None + case Some(Left(ex)) ⇒ throw ex + case Some(Right(v)) ⇒ Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) + } + } + + @deprecated("use new Future api instead", "2.0") + def asSilently[A](implicit m: Manifest[A]): Option[A] = { + try await catch { case _: FutureTimeoutException ⇒ } + future.value match { + case None ⇒ None + case Some(Left(ex)) ⇒ throw ex + case Some(Right(v)) ⇒ + try Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) + catch { case _: ClassCastException ⇒ None } + } + } + +} + +@deprecated("Await throws java.util.concurrent.TimeoutException", "2.0") +class FutureTimeoutException(message: String, cause: Throwable = null) extends AkkaException(message, cause) { + def this(message: String) = this(message, null) +} \ No newline at end of file diff --git a/akka-actor-migration/src/main/scala/akka/event/OldEventHandler.scala b/akka-actor-migration/src/main/scala/akka/event/OldEventHandler.scala new file mode 100644 index 0000000000..ef5846bc5c --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/event/OldEventHandler.scala @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ + +package akka.event + +import akka.actor.GlobalActorSystem + +/** + * Migration replacement for `akka.event.EventHandler` + */ +@deprecated("use Logging instead", "2.0") +object OldEventHandler { + + @deprecated("use Logging instead", "2.0") + def error(cause: Throwable, instance: AnyRef, message: ⇒ String) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isErrorEnabled) log.error(cause, message) + } + + @deprecated("use Logging instead", "2.0") + def error(cause: Throwable, instance: AnyRef, message: Any) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isErrorEnabled) log.error(cause, message.toString) + } + + @deprecated("use Logging instead", "2.0") + def error(instance: AnyRef, message: ⇒ String) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isErrorEnabled) log.error(message.toString) + } + + @deprecated("use Logging instead", "2.0") + def error(instance: AnyRef, message: Any) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isErrorEnabled) log.error(message.toString) + } + + @deprecated("use Logging instead", "2.0") + def warning(instance: AnyRef, message: ⇒ String) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isWarningEnabled) log.warning(message) + } + + @deprecated("use Logging instead", "2.0") + def warning(instance: AnyRef, message: Any) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isWarningEnabled) log.warning(message.toString) + } + + @deprecated("use Logging instead", "2.0") + def info(instance: AnyRef, message: ⇒ String) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isInfoEnabled) log.info(message) + } + + @deprecated("use Logging instead", "2.0") + def info(instance: AnyRef, message: Any) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isInfoEnabled) log.info(message.toString) + } + + @deprecated("use Logging instead", "2.0") + def debug(instance: AnyRef, message: ⇒ String) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isDebugEnabled) log.debug(message) + } + + @deprecated("use Logging instead", "2.0") + def debug(instance: AnyRef, message: Any) { + val log = Logging.getLogger(GlobalActorSystem, instance) + if (log.isDebugEnabled) log.debug(message.toString) + } + + @deprecated("use Logging instead", "2.0") + def isInfoEnabled = Logging.getLogger(GlobalActorSystem, this).isInfoEnabled + + @deprecated("use Logging instead", "2.0") + def isDebugEnabled = Logging.getLogger(GlobalActorSystem, this).isDebugEnabled + +} diff --git a/akka-actor-migration/src/main/scala/akka/migration/package.scala b/akka-actor-migration/src/main/scala/akka/migration/package.scala new file mode 100644 index 0000000000..319fdd997e --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/migration/package.scala @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka + +import akka.dispatch.Future +import akka.dispatch.OldFuture +import akka.util.Timeout +import akka.actor.GlobalActorSystem +import akka.dispatch.MessageDispatcher +import akka.actor.ActorRef + +package object migration { + + implicit def future2OldFuture[T](future: Future[T]): OldFuture[T] = new OldFuture[T](future) + + implicit def askTimeout: Timeout = GlobalActorSystem.settings.ActorTimeout + + implicit def defaultDispatcher: MessageDispatcher = GlobalActorSystem.dispatcher + + implicit def actorRef2OldActorRef(actorRef: ActorRef) = new OldActorRef(actorRef) + + class OldActorRef(actorRef: ActorRef) { + @deprecated("Actors are automatically started when creatd, i.e. remove old call to start()", "2.0") + def start(): ActorRef = actorRef + + @deprecated("Stop with ActorSystem or ActorContext instead", "2.0") + def exit() = stop() + + @deprecated("Stop with ActorSystem or ActorContext instead", "2.0") + def stop(): Unit = GlobalActorSystem.stop(actorRef) + } + +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/routing/Routing.scala b/akka-actor/src/main/scala/akka/routing/Routing.scala index 69589ae651..5e7c9ae701 100644 --- a/akka-actor/src/main/scala/akka/routing/Routing.scala +++ b/akka-actor/src/main/scala/akka/routing/Routing.scala @@ -515,7 +515,7 @@ object ScatterGatherFirstCompletedRouter { * be ignored if the 'nrOfInstances' is defined in the configuration file for the actor being used. */ case class ScatterGatherFirstCompletedRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, within: Duration, - override val resizer: Option[Resizer] = None) + override val resizer: Option[Resizer] = None) extends RouterConfig with ScatterGatherFirstCompletedLike { /** @@ -593,57 +593,57 @@ case class DefaultResizer( */ lowerBound: Int = 1, /** - * The most number of routees the router should ever have. - * Must be greater than or equal to `lowerBound`. - */ + * The most number of routees the router should ever have. + * Must be greater than or equal to `lowerBound`. + */ upperBound: Int = 10, /** - * Threshold to evaluate if routee is considered to be busy (under pressure). - * Implementation depends on this value (default is 1). - *

    - *
  • 0: number of routees currently processing a message.
  • - *
  • 1: number of routees currently processing a message has - * some messages in mailbox.
  • - *
  • > 1: number of routees with at least the configured `pressureThreshold` - * messages in their mailbox. Note that estimating mailbox size of - * default UnboundedMailbox is O(N) operation.
  • - *
- */ + * Threshold to evaluate if routee is considered to be busy (under pressure). + * Implementation depends on this value (default is 1). + *
    + *
  • 0: number of routees currently processing a message.
  • + *
  • 1: number of routees currently processing a message has + * some messages in mailbox.
  • + *
  • > 1: number of routees with at least the configured `pressureThreshold` + * messages in their mailbox. Note that estimating mailbox size of + * default UnboundedMailbox is O(N) operation.
  • + *
+ */ pressureThreshold: Int = 1, /** - * Percentage to increase capacity whenever all routees are busy. - * For example, 0.2 would increase 20% (rounded up), i.e. if current - * capacity is 6 it will request an increase of 2 more routees. - */ + * Percentage to increase capacity whenever all routees are busy. + * For example, 0.2 would increase 20% (rounded up), i.e. if current + * capacity is 6 it will request an increase of 2 more routees. + */ rampupRate: Double = 0.2, /** - * Minimum fraction of busy routees before backing off. - * For example, if this is 0.3, then we'll remove some routees only when - * less than 30% of routees are busy, i.e. if current capacity is 10 and - * 3 are busy then the capacity is unchanged, but if 2 or less are busy - * the capacity is decreased. - * - * Use 0.0 or negative to avoid removal of routees. - */ + * Minimum fraction of busy routees before backing off. + * For example, if this is 0.3, then we'll remove some routees only when + * less than 30% of routees are busy, i.e. if current capacity is 10 and + * 3 are busy then the capacity is unchanged, but if 2 or less are busy + * the capacity is decreased. + * + * Use 0.0 or negative to avoid removal of routees. + */ backoffThreshold: Double = 0.3, /** - * Fraction of routees to be removed when the resizer reaches the - * backoffThreshold. - * For example, 0.1 would decrease 10% (rounded up), i.e. if current - * capacity is 9 it will request an decrease of 1 routee. - */ + * Fraction of routees to be removed when the resizer reaches the + * backoffThreshold. + * For example, 0.1 would decrease 10% (rounded up), i.e. if current + * capacity is 9 it will request an decrease of 1 routee. + */ backoffRate: Double = 0.1, /** - * When the resizer reduce the capacity the abandoned routee actors are stopped - * with PoisonPill after this delay. The reason for the delay is to give concurrent - * messages a chance to be placed in mailbox before sending PoisonPill. - * Use 0 seconds to skip delay. - */ + * When the resizer reduce the capacity the abandoned routee actors are stopped + * with PoisonPill after this delay. The reason for the delay is to give concurrent + * messages a chance to be placed in mailbox before sending PoisonPill. + * Use 0 seconds to skip delay. + */ stopDelay: Duration = 1.second, /** - * Number of messages between resize operation. - * Use 1 to resize before each message. - */ + * Number of messages between resize operation. + * Use 1 to resize before each message. + */ messagesPerResize: Int = 10) extends Resizer { /** diff --git a/akka-docs/general/jmm.rst b/akka-docs/general/jmm.rst index 23871449ef..7d806f9ac8 100644 --- a/akka-docs/general/jmm.rst +++ b/akka-docs/general/jmm.rst @@ -1,3 +1,5 @@ +.. _jmm: + Akka and the Java Memory Model ================================ diff --git a/akka-docs/project/migration-guide-1.3.x-2.0.x.rst b/akka-docs/project/migration-guide-1.3.x-2.0.x.rst index 44fd51884c..0c8d239d03 100644 --- a/akka-docs/project/migration-guide-1.3.x-2.0.x.rst +++ b/akka-docs/project/migration-guide-1.3.x-2.0.x.rst @@ -4,6 +4,10 @@ Migration Guide 1.3.x to 2.0.x ################################ +.. sidebar:: Contents + + .. contents:: :local: + Actors ====== @@ -13,9 +17,177 @@ significant amount of time. Detailed migration guide will be written. +Migration Kit +============= + +Nobody likes a big refactoring that takes several days to complete until +anything is able to run again. Therefore we provide a migration kit that +makes it possible to do the migration changes in smaller steps. + +The migration kit only covers the most common usage of Akka. It is not intended +as a final solution. The whole migration kit is deprecated and will be removed in +Akka 2.1. + +The migration kit is provided in separate jar files. Add the following dependency:: + + "com.typesafe.akka" % "akka-actor-migration" % "2.0-SNAPSHOT" + +The first step of the migration is to do some trivial replacements. +Search and replace the following (be careful with the non qualified names): + +==================================== ==================================== +Search Replace with +==================================== ==================================== +``akka.actor.Actor`` ``akka.actor.OldActor`` +``extends Actor`` ``extends OldActor`` +``akka.actor.Scheduler`` ``akka.actor.OldScheduler`` +``Scheduler`` ``OldScheduler`` +``akka.event.EventHandler`` ``akka.event.OldEventHandler`` +``EventHandler`` ``OldEventHandler`` +``akka.config.Config`` ``akka.config.OldConfig`` +``Config`` ``OldConfig`` +==================================== ==================================== + +For Scala users the migration kit also contains some implicit conversions to be +able to use some old methods. These conversions are useful from tests or other +code used outside actors. + +:: + + import akka.migration._ + +Thereafter you need to fix compilation errors that are not handled by the migration +kit, such as: + +* Definition of supervisors +* Definition of dispatchers +* ActorRegistry + +When everything compiles you continue by replacing/removing the ``OldXxx`` classes +one-by-one from the migration kit with appropriate migration. + +When using the migration kit there will be one global actor system, which loads +the configuration ``akka.conf`` from the same locations as in Akka 1.x. +This means that while you are using the migration kit you should not create your +own ``ActorSystem``, but instead use the ``akka.actor.GlobalActorSystem``. +In order to voluntarily exit the JVM you must ``shutdown`` the ``GlobalActorSystem`` +Last task of the migration would be to create your own ``ActorSystem``. + + Unordered Collection of Migration Items ======================================= +Creating and starting actors +---------------------------- + +Actors are created by passing in a ``Props`` instance into the actorOf factory method in +a ``ActorRefProvider``, which is the ``ActorSystem`` or ``ActorContext``. +Use the system to create top level actors. Use the context to +create actors from other actors. The difference is how the supervisor hierarchy is arranged. +When using the context the current actor will be supervisor of the created child actor. +When using the system it will be a top level actor, that is supervised by the system +(internal guardian actor). + +``ActorRef.start()`` has been removed. Actors are now started automatically when created. +Remove all invocations of ``ActorRef.start()``. + +v1.3:: + + val myActor = Actor.actorOf[MyActor] + myActor.start() + +v2.0:: + + // top level actor + val firstActor = system.actorOf(Props[FirstActor], name = "first") + + // child actor + class FirstActor extends Actor { + val myActor = context.actorOf(Props[MyActor], name = "myactor") + +Documentation: + + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + +Stopping actors +--------------- + +``ActorRef.stop()`` has been moved. Use ``ActorSystem`` or ``ActorContext`` to stop actors. + +v1.3:: + + actorRef.stop() + self.stop() + actorRef ! PoisonPill + +v2.0:: + + context.stop(someChild) + context.stop(self) + system.stop(actorRef) + actorRef ! PoisonPill + +*Stop all actors* + +v1.3:: + + ActorRegistry.shutdownAll() + +v2.0:: + + system.shutdown() + +Documentation: + + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + +Identifying Actors +------------------ + +In v1.3 actors have ``uuid`` and ``id`` field. In v2.0 each actor has a unique logical ``path``. + +The ``ActorRegistry`` has been replaced by actor paths and lookup with +``actorFor`` in ``ActorRefProvider`` (``ActorSystem`` or ``ActorContext``). + +v1.3:: + + val actor = Actor.registry.actorFor(uuid) + val actors = Actor.registry.actorsFor(id) + +v2.0:: + + val actor = context.actorFor("/user/serviceA/aggregator") + +Documentation: + + * :ref:`addressing` + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + +Reply to messages +----------------- + +``self.channel`` has been replaced with unified reply mechanism using ``sender`` (Scala) +or ``getSender()`` (Java). This works for both tell (!) and ask (?). + +v1.3:: + + self.channel ! result + self.channel tryTell result + self.reply(result) + self.tryReply(result) + +v2.0:: + + sender ! result + +Documentation: + + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + ``ActorRef.ask()`` ------------------ @@ -28,7 +200,185 @@ reply to be received; it is independent of the timeout applied when awaiting completion of the :class:`Future`, however, the actor will complete the :class:`Future` with an :class:`AskTimeoutException` when it stops itself. +Documentation: + + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + ActorPool --------- The ActorPool has been replaced by dynamically resizable routers. + +Documentation: + + * :ref:`routing-scala` + * :ref:`routing-java` + +``UntypedActor.getContext()`` (Java API only) +--------------------------------------------- + +``getContext()`` in the Java API for UntypedActor is renamed to +``getSelf()``. + +v1.3:: + + actorRef.tell("Hello", getContext()); + +v2.0:: + + actorRef.tell("Hello", getSelf()); + +Documentation: + + * :ref:`untyped-actors-java` + +Logging +------- + +EventHandler API has been replaced by LoggingAdapter, which publish log messages +to the event bus. You can still plugin your own actor as event listener with the +``akka.event-handlers`` configuration property. + +v1.3:: + + EventHandler.error(exception, this, message) + EventHandler.warning(this, message) + EventHandler.info(this, message) + EventHandler.debug(this, message) + EventHandler.debug(this, "Processing took %s ms".format(duration)) + +v2.0:: + + import akka.event.Logging + + val log = Logging(context.system, this) + log.error(exception, this, message) + log.warning(this, message) + log.info(this, message) + log.debug(this, message) + log.debug(this, "Processing took {} ms", duration) + +Documentation: + + * :ref:`logging-scala` + * :ref:`logging-java` + * :ref:`event-bus-scala` + * :ref:`event-bus-java` + +Supervision +----------- + +Akka v2.0 implements parental supervision. Actors can only be created by other actors — where the top-level +actor is provided by the library — and each created actor is supervised by its parent. +In contrast to the special supervision relationship between parent and child, each actor may monitor any +other actor for termination. + +v1.3:: + + self.link(actorRef) + self.unlink(actorRef) + +v2.0:: + + class WatchActor extends Actor { + val actorRef = ... + // Terminated message will be delivered when the actorRef actor + // is stopped + context.watch(actorRef) + + val supervisedChild = context.actorOf(Props[ChildActor]) + + def receive = { + case Terminated(`actorRef`) ⇒ ... + } + } + +Note that ``link`` in v1.3 established a supervision relation, which ``watch`` doesn't. +``watch`` is only a way to get notification, ``Terminated`` message, when the monitored +actor has been stopped. + +*Refererence to the supervisor* + +v1.3:: + + self.supervisor + +v2.0:: + + context.parent + +*Fault handling strategy* + +v1.3:: + + val supervisor = Supervisor( + SupervisorConfig( + AllForOneStrategy(List(classOf[Exception]), 3, 1000), + Supervise( + actorOf[MyActor1], + Permanent) :: + Supervise( + actorOf[MyActor2], + Permanent) :: + Nil)) + +v2.0:: + + val strategy = OneForOneStrategy({ + case _: ArithmeticException ⇒ Resume + case _: NullPointerException ⇒ Restart + case _: IllegalArgumentException ⇒ Stop + case _: Exception ⇒ Escalate + }: Decider, maxNrOfRetries = Some(10), withinTimeRange = Some(60000)) + + val supervisor = system.actorOf(Props[Supervisor].withFaultHandler(strategy), "supervisor") + +Documentation: + + * :ref:`supervision` + * :ref:`fault-tolerance-java` + * :ref:`fault-tolerance-scala` + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + +Spawn +----- + +``spawn`` has been removed and can be implemented like this, if needed. Be careful to not +access any shared mutable state closed over by the body. + +:: + + def spawn(body: ⇒ Unit) { + system.actorOf(Props(ctx ⇒ { case "go" ⇒ try body finally ctx.stop(ctx.self) })) ! "go" + } + +Documentation: + + * :ref:`jmm` + +HotSwap +------- + +In v2.0 ``become`` and ``unbecome`` metods are located in ``ActorContext``, i.e. ``context.become`` and ``context.unbecome``. + +The special ``HotSwap`` and ``RevertHotswap`` messages in v1.3 has been removed. Similar can be +implemented with your own message and using ``context.become`` and ``context.unbecome`` +in the actor receiving the message. + + * :ref:`actors-scala` + * :ref:`untyped-actors-java` + +More to be written +------------------ + +* Futures +* Dispatchers +* STM +* TypedActors +* Routing +* Remoting +* Scheduler +* Configuration +* ...? \ No newline at end of file diff --git a/akka-docs/scala/code/akka/docs/actor/ActorDocSpec.scala b/akka-docs/scala/code/akka/docs/actor/ActorDocSpec.scala index 20ac33480b..36439d2a16 100644 --- a/akka-docs/scala/code/akka/docs/actor/ActorDocSpec.scala +++ b/akka-docs/scala/code/akka/docs/actor/ActorDocSpec.scala @@ -7,6 +7,9 @@ package akka.docs.actor import akka.actor.Actor import akka.actor.Props import akka.event.Logging + +//#imports1 + import akka.dispatch.Future import akka.actor.ActorSystem import org.scalatest.{ BeforeAndAfterAll, WordSpec } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 8f6e656e46..7b6808a944 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -30,7 +30,7 @@ object AkkaBuild extends Build { Unidoc.unidocExclude := Seq(samples.id, tutorials.id), Dist.distExclude := Seq(actorTests.id, akkaSbtPlugin.id, docs.id) ), - aggregate = Seq(actor, testkit, actorTests, remote, slf4j, agent, transactor, mailboxes, kernel, akkaSbtPlugin, samples, tutorials, docs) + aggregate = Seq(actor, testkit, actorTests, remote, slf4j, agent, transactor, mailboxes, kernel, akkaSbtPlugin, actorMigration, samples, tutorials, docs) ) lazy val actor = Project( @@ -212,6 +212,13 @@ object AkkaBuild extends Build { ) ) + lazy val actorMigration = Project( + id = "akka-actor-migration", + base = file("akka-actor-migration"), + dependencies = Seq(actor, testkit % "test->test"), + settings = defaultSettings + ) + lazy val akkaSbtPlugin = Project( id = "akka-sbt-plugin", base = file("akka-sbt-plugin"),