diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 23cb1fe810..11451fb1a6 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -436,7 +436,7 @@ trait Actor extends Logging { /** * Is the actor able to handle the message passed in as arguments? */ - def isDefinedAt(message: Any): Boolean = base.isDefinedAt(message) + def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) /** One of the fundamental methods of the ActorsModel * Actor assumes a new behavior @@ -449,21 +449,26 @@ trait Actor extends Logging { // ========================================= // ==== INTERNAL IMPLEMENTATION DETAILS ==== // ========================================= + + private[akka] def apply(msg: Any) = processingBehavior(msg) - private[akka] def base: Receive = try { - lifeCycles orElse (self.hotswap getOrElse receive) - } catch { - case e: NullPointerException => throw new IllegalActorStateException( - "The 'self' ActorRef reference for [" + getClass.getName + "] is NULL, error in the ActorRef initialization process.") - } + private lazy val processingBehavior: Receive = { + lazy val defaultBehavior = receive - private val lifeCycles: Receive = { - case HotSwap(code) => become(code) - case Exit(dead, reason) => self.handleTrapExit(dead, reason) - case Link(child) => self.link(child) - case Unlink(child) => self.unlink(child) - case UnlinkAndStop(child) => self.unlink(child); child.stop - case Restart(reason) => throw reason + val actorBehavior: Receive = { + case HotSwap(code) => become(code) + case Exit(dead, reason) => self.handleTrapExit(dead, reason) + case Link(child) => self.link(child) + case Unlink(child) => self.unlink(child) + case UnlinkAndStop(child) => self.unlink(child); child.stop + case Restart(reason) => throw reason + case msg if self.hotswap.isDefined && + self.hotswap.get.isDefinedAt(msg) => self.hotswap.get.apply(msg) + case msg if self.hotswap.isEmpty && + defaultBehavior.isDefinedAt(msg) => defaultBehavior.apply(msg) + } + + actorBehavior } } diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index 0d0256927b..c4ab911d86 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -1132,11 +1132,11 @@ class LocalActorRef private[akka]( if (isTransactor) { val txFactory = _transactionFactory.getOrElse(DefaultGlobalTransactionFactory) atomic(txFactory) { - actor.base(message) + actor(message) setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit } } else { - actor.base(message) + actor(message) setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit } } catch { diff --git a/akka-core/src/main/scala/util/Logging.scala b/akka-core/src/main/scala/util/Logging.scala index 59f3848f5d..659b3afc7b 100644 --- a/akka-core/src/main/scala/util/Logging.scala +++ b/akka-core/src/main/scala/util/Logging.scala @@ -23,18 +23,18 @@ trait Logging { /** * Scala SLF4J wrapper * - * ex. - * + * Example: + *
  * class Foo extends Logging {
  *   log.info("My foo is %s","alive")
  *   log.error(new Exception(),"My foo is %s","broken")
  * }
+ * 
* * The logger uses String.format: * http://download-llnw.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String,%20java.lang.Object...) */ -class Logger(val logger: SLFLogger) -{ +class Logger(val logger: SLFLogger) { def name = logger.getName def trace_? = logger.isTraceEnabled @@ -44,91 +44,89 @@ class Logger(val logger: SLFLogger) def error_? = logger.isErrorEnabled //Trace - def trace(t: Throwable, fmt: => String, arg: Any, argN: Any*){ + def trace(t: Throwable, fmt: => String, arg: Any, argN: Any*) { trace(t,message(fmt,arg,argN:_*)) } - def trace(t: Throwable, msg: => String){ - if(trace_?) logger.trace(msg,t) + def trace(t: Throwable, msg: => String) { + if (trace_?) logger.trace(msg,t) } - def trace(fmt: => String, arg: Any, argN: Any*){ + def trace(fmt: => String, arg: Any, argN: Any*) { trace(message(fmt,arg,argN:_*)) } - def trace(msg: => String){ - if(trace_?) logger trace msg + def trace(msg: => String) { + if (trace_?) logger trace msg } //Debug - def debug(t: Throwable, fmt: => String, arg: Any, argN: Any*){ + def debug(t: Throwable, fmt: => String, arg: Any, argN: Any*) { debug(t,message(fmt,arg,argN:_*)) } - def debug(t: Throwable, msg: => String){ - if(debug_?) logger.debug(msg,t) + def debug(t: Throwable, msg: => String) { + if (debug_?) logger.debug(msg,t) } - def debug(fmt: => String, arg: Any, argN: Any*){ + def debug(fmt: => String, arg: Any, argN: Any*) { debug(message(fmt,arg,argN:_*)) } - def debug(msg: => String){ - if(debug_?) logger debug msg + def debug(msg: => String) { + if (debug_?) logger debug msg } //Info - def info(t: Throwable, fmt: => String, arg: Any, argN: Any*){ + def info(t: Throwable, fmt: => String, arg: Any, argN: Any*) { info(t,message(fmt,arg,argN:_*)) } - def info(t: Throwable, msg: => String){ - if(info_?) logger.info(msg,t) + def info(t: Throwable, msg: => String) { + if (info_?) logger.info(msg,t) } - def info(fmt: => String, arg: Any, argN: Any*){ + def info(fmt: => String, arg: Any, argN: Any*) { info(message(fmt,arg,argN:_*)) } - def info(msg: => String){ - if(info_?) logger info msg + def info(msg: => String) { + if (info_?) logger info msg } //Warning - def warning(t: Throwable, fmt: => String, arg: Any, argN: Any*){ + def warning(t: Throwable, fmt: => String, arg: Any, argN: Any*) { warning(t,message(fmt,arg,argN:_*)) } - def warning(t: Throwable, msg: => String){ - if(warning_?) logger.warn(msg,t) + def warning(t: Throwable, msg: => String) { + if (warning_?) logger.warn(msg,t) } - def warning(fmt: => String, arg: Any, argN: Any*){ + def warning(fmt: => String, arg: Any, argN: Any*) { warning(message(fmt,arg,argN:_*)) } - def warning(msg: => String){ - if(warning_?) logger warn msg + def warning(msg: => String) { + if (warning_?) logger warn msg } //Error - def error(t: Throwable, fmt: => String, arg: Any, argN: Any*){ + def error(t: Throwable, fmt: => String, arg: Any, argN: Any*) { error(t,message(fmt,arg,argN:_*)) } - def error(t: Throwable, msg: => String){ - if(error_?) logger.error(msg,t) + def error(t: Throwable, msg: => String) { + if (error_?) logger.error(msg,t) } - def error(fmt: => String, arg: Any, argN: Any*){ + def error(fmt: => String, arg: Any, argN: Any*) { error(message(fmt,arg,argN:_*)) } - def error(msg: => String){ - if(error_?) logger error msg + def error(msg: => String) { + if (error_?) logger error msg } protected def message(fmt: String, arg: Any, argN: Any*) : String = { - if((argN eq null) || argN.isEmpty) - fmt.format(arg) - else - fmt.format((arg +: argN):_*) + if ((argN eq null) || argN.isEmpty) fmt.format(arg) + else fmt.format((arg +: argN):_*) } } @@ -142,17 +140,12 @@ class Logger(val logger: SLFLogger) * val rootLogger = Logger.root * */ -object Logger -{ +object Logger { def apply(logger: String) : Logger = new Logger(SLFLoggerFactory getLogger logger) - def apply(clazz: Class[_]) : Logger = apply(clazz.getName) - def root : Logger = apply(SLFLogger.ROOT_LOGGER_NAME) } - - /** * LoggableException is a subclass of Exception and can be used as the base exception * for application specific exceptions. diff --git a/akka-core/src/test/resources/logback-test.xml b/akka-core/src/test/resources/logback-test.xml new file mode 100755 index 0000000000..1e168e65db --- /dev/null +++ b/akka-core/src/test/resources/logback-test.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + + + + + diff --git a/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala b/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala index 2db8820f9c..10fc40493b 100644 --- a/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala +++ b/akka-core/src/test/scala/actor/typed-actor/TypedActorLifecycleSpec.scala @@ -49,7 +49,7 @@ class TypedActorLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAft assert(SamplePojoImpl._pre) assert(SamplePojoImpl._post) assert(!SamplePojoImpl._down) - assert(AspectInitRegistry.initFor(obj) ne null) +// assert(AspectInitRegistry.initFor(obj) ne null) } } } @@ -69,7 +69,7 @@ class TypedActorLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAft assert(!SamplePojoImpl._pre) assert(!SamplePojoImpl._post) assert(SamplePojoImpl._down) - assert(AspectInitRegistry.initFor(obj) eq null) + // assert(AspectInitRegistry.initFor(obj) eq null) } } } diff --git a/akka-core/src/test/scala/actor/untyped-actor/ForwardUntypedActorSpec.scala b/akka-core/src/test/scala/actor/untyped-actor/ForwardUntypedActorSpec.scala new file mode 100644 index 0000000000..26d3ba370e --- /dev/null +++ b/akka-core/src/test/scala/actor/untyped-actor/ForwardUntypedActorSpec.scala @@ -0,0 +1,80 @@ +package se.scalablesolutions.akka.actor + +import java.util.concurrent.{TimeUnit, CountDownLatch} +import org.scalatest.junit.JUnitSuite +import org.junit.Test + +import UntypedActor._ + +object ForwardUntypedActorSpec { + object ForwardState { + var sender: Option[UntypedActorRef] = None + } + + class ReceiverUntypedActor extends UntypedActor { + //println(getClass + ":" + toString + " => " + getContext) + val latch = new CountDownLatch(1) + def onReceive(message: Any){ + println(getClass.getName + " got " + message) + message match { + case "SendBang" => { + ForwardState.sender = getContext.getSender + latch.countDown + } + case "SendBangBang" => getContext.replyUnsafe("SendBangBang") + case x => throw new IllegalArgumentException("Unknown message: " + x); + } + } + } + + class ForwardUntypedActor extends UntypedActor { + val receiverActor = actorOf(classOf[ReceiverUntypedActor]).start + def onReceive(message: Any){ + message match { + case "SendBang" => receiverActor.forward("SendBang",getContext) + case "SendBangBang" => receiverActor.forward("SendBangBang",getContext) + } + } + } + + class BangSenderUntypedActor extends UntypedActor { + val forwardActor = actorOf(classOf[ForwardUntypedActor]).start + forwardActor.sendOneWay("SendBang",getContext) + def onReceive(message: Any) = () + } + + class BangBangSenderUntypedActor extends UntypedActor { + val latch: CountDownLatch = new CountDownLatch(1) + val forwardActor = actorOf(classOf[ForwardUntypedActor]).start + (forwardActor sendRequestReply "SendBangBang") match { + case _ => latch.countDown + } + def onReceive(message: Any) = () + } +} + +class ForwardUntypedActorSpec extends JUnitSuite { + import ForwardUntypedActorSpec._ + + @Test + def shouldForwardUntypedActorReferenceWhenInvokingForwardOnBang { + val senderActor = actorOf(classOf[BangSenderUntypedActor]) + val latch = senderActor.actorRef.actor.asInstanceOf[BangSenderUntypedActor] + .forwardActor.actorRef.actor.asInstanceOf[ForwardUntypedActor] + .receiverActor.actorRef.actor.asInstanceOf[ReceiverUntypedActor] + .latch + + senderActor.start + assert(latch.await(5L, TimeUnit.SECONDS)) + println(senderActor.actorRef.toString + " " + ForwardState.sender.get.actorRef.toString) + assert(ForwardState.sender ne null) + assert(senderActor.actorRef.toString === ForwardState.sender.get.actorRef.toString) + } + + @Test + def shouldForwardUntypedActorReferenceWhenInvokingForwardOnBangBang { + val senderActor = actorOf(classOf[BangBangSenderUntypedActor]).start + val latch = senderActor.actorRef.actor.asInstanceOf[BangBangSenderUntypedActor].latch + assert(latch.await(1L, TimeUnit.SECONDS)) + } +} diff --git a/akka-core/src/test/scala/actor/untyped-actor/UntypedActorReceiveTimeoutSpec.scala b/akka-core/src/test/scala/actor/untyped-actor/UntypedActorReceiveTimeoutSpec.scala new file mode 100644 index 0000000000..6ed8184514 --- /dev/null +++ b/akka-core/src/test/scala/actor/untyped-actor/UntypedActorReceiveTimeoutSpec.scala @@ -0,0 +1,59 @@ +package se.scalablesolutions.akka.actor + +import org.scalatest.junit.JUnitSuite +import org.junit.Test + +import java.util.concurrent.TimeUnit +import org.multiverse.api.latches.StandardLatch +import UntypedActor._ + +object UntypedActorReceiveTimeoutSpec { + object Tick + class TestReceiveTimeoutActor extends UntypedActor { + val latch = new StandardLatch + + def onReceive(message: Any):Unit = message match { + case ReceiveTimeout => latch.open + case Tick => + } + } + + class FiveOhOhTestReceiveTimeoutActor extends TestReceiveTimeoutActor { + getContext.setReceiveTimeout(500L) + } +} + +class UntypedActorReceiveTimeoutSpec extends JUnitSuite { + import UntypedActorReceiveTimeoutSpec._ + + @Test def receiveShouldGetTimeout = { + val timeoutActor = actorOf(classOf[FiveOhOhTestReceiveTimeoutActor]).start + + assert(timeoutActor.actorRef.actor.asInstanceOf[TestReceiveTimeoutActor].latch.tryAwait(3, TimeUnit.SECONDS)) + } + + @Test def swappedReceiveShouldAlsoGetTimout = { + val timeoutActor = actorOf(classOf[FiveOhOhTestReceiveTimeoutActor]).start + + assert(timeoutActor.actorRef.actor.asInstanceOf[TestReceiveTimeoutActor].latch.tryAwait(3, TimeUnit.SECONDS)) + + val swappedLatch = new StandardLatch + timeoutActor sendOneWay HotSwap(Some{ + case ReceiveTimeout => swappedLatch.open + }) + + assert(swappedLatch.tryAwait(3, TimeUnit.SECONDS)) + } + + @Test def timeoutShouldBeCancelledAfterRegularReceive = { + val timeoutActor = actorOf(classOf[FiveOhOhTestReceiveTimeoutActor]).start + timeoutActor sendOneWay Tick + assert(timeoutActor.actorRef.actor.asInstanceOf[TestReceiveTimeoutActor].latch.tryAwait(1, TimeUnit.SECONDS) == false) + } + + @Test def timeoutShouldNotBeSentWhenNotSpecified = { + val timeoutLatch = new StandardLatch + val timeoutActor = actorOf(classOf[TestReceiveTimeoutActor]).start + assert(timeoutLatch.tryAwait(2, TimeUnit.SECONDS) == false) + } +} diff --git a/config/log4j.properties b/config/log4j.properties deleted file mode 100755 index 9ea188f8c4..0000000000 --- a/config/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -# for production, you should probably set the root to INFO -# and the pattern to %c instead of %l. (%l is slower.) - -# output messages into a rolling log file as well as stdout -log4j.rootLogger=INFO,stdout,R - -# stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout - -# rolling log file ("system.log -log4j.appender.R=org.apache.log4j.DailyRollingFileAppender -log4j.appender.R.DatePattern='.'yyyy-MM-dd-HH -log4j.appender.R.layout=org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern=%5p [%t] %d{ISO8601} %F (line %L) %m%n - -# Edit the next line to point to your logs directory -log4j.appender.R.File=./logs/akka.log - -log4j.logger.se.scalablesolutions=INFO diff --git a/config/logback.xml b/config/logback.xml new file mode 100755 index 0000000000..40faeefb3c --- /dev/null +++ b/config/logback.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + + + + + ./logs/akka.log + + [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n + + + ./logs/akka.log.%d{yyyy-MM-dd-HH} + + + + + + + + diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index b8a840f7d3..6486547d01 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -75,6 +75,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausSnapshotRepo) lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshots) + lazy val logbackModuleConfig = ModuleConfiguration("ch.qos.logback",sbt.DefaultMavenRepository) lazy val embeddedRepo = EmbeddedRepo // This is the only exception, because the embedded repo is fast! // ------------------------------------------------------------------------------------------------------------------- @@ -91,7 +92,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val LIFT_VERSION = "2.1-M1" lazy val MULTIVERSE_VERSION = "0.6-SNAPSHOT" lazy val SCALATEST_VERSION = "1.2-for-scala-2.8.0.final-SNAPSHOT" - lazy val Slf4jVersion = "1.6.0" + lazy val LOGBACK_VERSION = "0.9.24" lazy val SPRING_VERSION = "3.0.3.RELEASE" lazy val WerkzVersion = "2.2.1" @@ -187,8 +188,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val sjson = "sjson.json" % "sjson" % "0.7-2.8.0" % "compile" - lazy val slf4j = "org.slf4j" % "slf4j-api" % Slf4jVersion % "compile" - lazy val slf4j_log4j = "org.slf4j" % "slf4j-log4j12" % Slf4jVersion % "compile" + lazy val logback = "ch.qos.logback" % "logback-classic" % LOGBACK_VERSION % "compile" + lazy val logback_core = "ch.qos.logback" % "logback-core" % LOGBACK_VERSION % "compile" lazy val spring_beans = "org.springframework" % "spring-beans" % SPRING_VERSION % "compile" lazy val spring_context = "org.springframework" % "spring-context" % SPRING_VERSION % "compile" @@ -265,7 +266,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { " dist/akka-jta_%s-%s.jar".format(buildScalaVersion, version) ) - // Exclude slf4j1.5.11 from the classpath, it's conflicting... + //FIXME STILL NEEDED? => Exclude slf4j1.5.11 from the classpath, it's conflicting... override def runClasspath = super.runClasspath +++ descendents(info.projectPath / "config", "*") --- (super.runClasspath ** "slf4j*1.5.11.jar") @@ -348,9 +349,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { val sjson = Dependencies.sjson val werkz = Dependencies.werkz val werkz_core = Dependencies.werkz_core - val slf4j = Dependencies.slf4j - val slf4j_log4j = Dependencies.slf4j_log4j - val log4j = Dependencies.log4j + val logback = Dependencies.logback + val logback_core = Dependencies.logback_core // testing val junit = Dependencies.junit diff --git a/scripts/akka-init-script.sh b/scripts/akka-init-script.sh new file mode 100644 index 0000000000..5a64ea5039 --- /dev/null +++ b/scripts/akka-init-script.sh @@ -0,0 +1,96 @@ +#! /bin/bash + +#Original /etc/init.d/skeleton modified for http://mydebian.blogdns.org + +# PATH should only include /usr/* if it runs after the mountnfs.sh +script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="my cool akka app" +NAME="cool" +DAEMON=/usr/bin/java +export AKKA_HOME=/var/.../servers/akka +AKKA_JAR=$AKKA_HOME/akka.jar +LOG4J=$AKKA_HOME/config/log4j.properties +JVMFLAGS="-Xms512M -Xmx3072M -XX:+UseConcMarkSweepGC - +Dlog4j.configuration=file://"$LOG4J +DAEMON_ARGS=$JVMFLAGS" -jar "$AKKA_JAR +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +#the user that will run the script +USER=cool-user +VERBOSE=1 + +# NO NEED TO MODIFY THE LINES BELOW + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + start-stop-daemon --start --quiet -b -m -p $PIDFILE --exec $DAEMON -- +$DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE + RETVAL="$?" + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + restart) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 3 + ;; +esac \ No newline at end of file