From 6f1a9d2afdcb6430bd7a71d74c81183c1789ec78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Thu, 18 Feb 2010 21:38:53 +0100 Subject: [PATCH 01/81] updated to 0.4 multiverse --- akka-util-java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-util-java/pom.xml b/akka-util-java/pom.xml index e0a729491b..3daa008792 100644 --- a/akka-util-java/pom.xml +++ b/akka-util-java/pom.xml @@ -27,7 +27,7 @@ org.multiverse multiverse-alpha - 0.3 + 0.4-SNAPSHOT jar-with-dependencies From 7a40f1a8cdfa28fd19d80a462423a44876879264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Mon, 22 Feb 2010 13:22:10 +0100 Subject: [PATCH 02/81] upgraded to multiverse 0.4-SNAPSHOT --- akka-core/pom.xml | 28 ++ akka-core/src/main/scala/actor/Actor.scala | 8 +- .../src/main/scala/stm/Transaction.scala | 132 ++++--- .../scala/stm/TransactionManagement.scala | 4 +- .../main/scala/stm/TransactionalState.scala | 5 +- akka-util-java/pom.xml | 28 -- .../akka/stm/AtomicTemplate.java | 341 ------------------ pom.xml | 52 ++- 8 files changed, 136 insertions(+), 462 deletions(-) delete mode 100644 akka-util-java/src/main/java/se/scalablesolutions/akka/stm/AtomicTemplate.java diff --git a/akka-core/pom.xml b/akka-core/pom.xml index d6ca57ebfe..5d58430aa6 100644 --- a/akka-core/pom.xml +++ b/akka-core/pom.xml @@ -46,6 +46,34 @@ netty 3.2.0.ALPHA3 + + org.multiverse + multiverse-alpha + 0.4-SNAPSHOT + jar-with-dependencies + + + org.multiverse + multiverse-core + + + asm + asm-tree + + + asm + asm-analysis + + + asm + asm-commons + + + asm + asm-util + + + org.scala-tools javautils diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 41b2673bfa..a6d9231b10 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -911,10 +911,10 @@ trait Actor extends TransactionManagement { try { if (isTransactionRequiresNew && !isTransactionInScope) { - if (senderFuture.isEmpty) throw new StmException( - "Can't continue transaction in a one-way fire-forget message send" + - "\n\tE.g. using Actor '!' method or Active Object 'void' method" + - "\n\tPlease use the Actor '!!' method or Active Object method with non-void return type") + //if (senderFuture.isEmpty) throw new StmException( + // "Can't continue transaction in a one-way fire-forget message send" + + // "\n\tE.g. using Actor '!' method or Active Object 'void' method" + + // "\n\tPlease use the Actor '!!' method or Active Object method with non-void return type") atomic { proceed } diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 1637b4c906..d535f98433 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -13,9 +13,10 @@ import se.scalablesolutions.akka.util.Logging import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance import org.multiverse.api.ThreadLocalTransaction._ -import org.multiverse.templates.OrElseTemplate +import org.multiverse.commitbarriers.VetoCommitBarrier import scala.collection.mutable.HashMap +import org.multiverse.templates.{TransactionTemplate, OrElseTemplate} class NoTransactionInScopeException extends RuntimeException class TransactionRetryException(message: String) extends RuntimeException(message) @@ -30,8 +31,8 @@ class TransactionRetryException(message: String) extends RuntimeException(messag * Here are some examples (assuming implicit transaction family name in scope): *
  * import se.scalablesolutions.akka.stm.Transaction._
- * 
- * atomic {
+ *
+ * atomic  {
  *   .. // do something within a transaction
  * }
  * 
@@ -39,8 +40,8 @@ class TransactionRetryException(message: String) extends RuntimeException(messag * Example of atomic transaction management using atomic block with retry count: *
  * import se.scalablesolutions.akka.stm.Transaction._
- * 
- * atomic(maxNrOfRetries) {
+ *
+ * atomic(maxNrOfRetries)  {
  *   .. // do something within a transaction
  * }
  * 
@@ -49,10 +50,10 @@ class TransactionRetryException(message: String) extends RuntimeException(messag * Which is a good way to reduce contention and transaction collisions. *
  * import se.scalablesolutions.akka.stm.Transaction._
- * 
- * atomically {
+ *
+ * atomically  {
  *   .. // try to do something
- * } orElse {
+ * } orElse  {
  *   .. // if transaction clashes try do do something else to minimize contention
  * }
  * 
@@ -61,11 +62,11 @@ class TransactionRetryException(message: String) extends RuntimeException(messag * *
  * import se.scalablesolutions.akka.stm.Transaction._
- * for (tx <- Transaction) {
+ * for (tx <- Transaction)  {
  *   ... // do transactional stuff
  * }
  *
- * val result = for (tx <- Transaction) yield {
+ * val result = for (tx <- Transaction) yield  {
  *   ... // do transactional stuff yielding a result
  * }
  * 
@@ -78,17 +79,17 @@ class TransactionRetryException(message: String) extends RuntimeException(messag * * // You can use them together with Transaction in a for comprehension since * // TransactionalRef is also monadic - * for { + * for { * tx <- Transaction * ref <- refs * } { * ... // use the ref inside a transaction * } * - * val result = for { + * val result = for { * tx <- Transaction * ref <- refs - * } yield { + * } yield { * ... // use the ref inside a transaction, yield a result * } * @@ -101,57 +102,61 @@ object Transaction extends TransactionManagement { /** * See ScalaDoc on class. */ - def map[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic { f(getTransactionInScope) } + def map[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic {f(getTransactionInScope)} /** * See ScalaDoc on class. */ - def flatMap[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic { f(getTransactionInScope) } + def flatMap[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic {f(getTransactionInScope)} /** * See ScalaDoc on class. */ - def foreach(f: Transaction => Unit)(implicit transactionFamilyName: String): Unit = atomic { f(getTransactionInScope) } + def foreach(f: Transaction => Unit)(implicit transactionFamilyName: String): Unit = atomic {f(getTransactionInScope)} /** * Creates a "pure" STM atomic transaction and by-passes all transactions hooks * such as persistence etc. * Only for internal usage. */ - private[akka] def pureAtomic[T](body: => T): T = new AtomicTemplate[T]( - getGlobalStmInstance, "internal", false, false, TransactionManagement.MAX_NR_OF_RETRIES) { + private[akka] def pureAtomic[T](body: => T): T = new TransactionTemplate[T]() { def execute(mtx: MultiverseTransaction): T = body }.execute() /** * See ScalaDoc on class. */ - def atomic[T](body: => T)(implicit transactionFamilyName: String): T = new AtomicTemplate[T]( - getGlobalStmInstance, transactionFamilyName, false, false, TransactionManagement.MAX_NR_OF_RETRIES) { - def execute(mtx: MultiverseTransaction): T = body - override def postStart(mtx: MultiverseTransaction) = { - val tx = new Transaction - tx.transaction = Some(mtx) - setTransaction(Some(tx)) - } - override def postCommit = { - if (isTransactionInScope) getTransactionInScope.commit - else throw new IllegalStateException("No transaction in scope") - } - }.execute() + def atomic[T](body: => T)(implicit transactionFamilyName: String): T = { + new TransactionTemplate[T]() { // FIXME take factory + def execute(mtx: MultiverseTransaction): T = body + + override def onStart(mtx: MultiverseTransaction) = { + val tx = new Transaction + tx.transaction = Some(mtx) + setTransaction(Some(tx)) + } + + override def onPostCommit = { + if (isTransactionInScope) getTransactionInScope.commit + else throw new IllegalStateException("No transaction in scope") + } + }.execute() + } /** * See ScalaDoc on class. */ def atomic[T](retryCount: Int)(body: => T)(implicit transactionFamilyName: String): T = { - new AtomicTemplate[T](getGlobalStmInstance, transactionFamilyName, false, false, retryCount) { + new TransactionTemplate[T]() { // FIXME take factory def execute(mtx: MultiverseTransaction): T = body - override def postStart(mtx: MultiverseTransaction) = { + + override def onStart(mtx: MultiverseTransaction) = { val tx = new Transaction tx.transaction = Some(mtx) setTransaction(Some(tx)) } - override def postCommit = { + + override def onPostCommit = { if (isTransactionInScope) getTransactionInScope.commit else throw new IllegalStateException("No transaction in scope") } @@ -162,14 +167,16 @@ object Transaction extends TransactionManagement { * See ScalaDoc on class. */ def atomicReadOnly[T](retryCount: Int)(body: => T)(implicit transactionFamilyName: String): T = { - new AtomicTemplate[T](getGlobalStmInstance, transactionFamilyName, false, true, retryCount) { + new TransactionTemplate[T]() { // FIXME take factory def execute(mtx: MultiverseTransaction): T = body - override def postStart(mtx: MultiverseTransaction) = { + + override def onStart(mtx: MultiverseTransaction) = { val tx = new Transaction tx.transaction = Some(mtx) setTransaction(Some(tx)) } - override def postCommit = { + + override def onPostCommit = { if (isTransactionInScope) getTransactionInScope.commit else throw new IllegalStateException("No transaction in scope") } @@ -180,14 +187,16 @@ object Transaction extends TransactionManagement { * See ScalaDoc on class. */ def atomicReadOnly[T](body: => T): T = { - new AtomicTemplate[T](true) { + new TransactionTemplate[T]() { // FIXME take factory def execute(mtx: MultiverseTransaction): T = body - override def postStart(mtx: MultiverseTransaction) = { + + override def onStart(mtx: MultiverseTransaction) = { val tx = new Transaction tx.transaction = Some(mtx) setTransaction(Some(tx)) } - override def postCommit = { + + override def onPostCommit = { if (isTransactionInScope) getTransactionInScope.commit else throw new IllegalStateException("No transaction in scope") } @@ -206,6 +215,7 @@ object Transaction extends TransactionManagement { def elseBody[A](firstBody: => A) = new { def orElse(secondBody: => A) = new OrElseTemplate[A] { def run(t: MultiverseTransaction) = firstBody + def orelserun(t: MultiverseTransaction) = secondBody }.execute() } @@ -216,30 +226,38 @@ object Transaction extends TransactionManagement { */ @serializable class Transaction extends Logging { import Transaction._ - + val id = Transaction.idFactory.incrementAndGet @volatile private[this] var status: TransactionStatus = TransactionStatus.New private[akka] var transaction: Option[MultiverseTransaction] = None private[this] val persistentStateMap = new HashMap[String, Committable] private[akka] val depth = new AtomicInteger(0) - + //private[akka] val transactionSet = new VetoCommitBarrier + + //transactionSet.vetoCommit(this) + // --- public methods --------- + def abort = synchronized { + // transactionSet.abort + } + def commit = synchronized { pureAtomic { persistentStateMap.values.foreach(_.commit) TransactionManagement.clearTransaction } + //transactionSet.vetoCommit(this) status = TransactionStatus.Completed } - def isNew = synchronized { status == TransactionStatus.New } + def isNew = synchronized {status == TransactionStatus.New} - def isActive = synchronized { status == TransactionStatus.Active } + def isActive = synchronized {status == TransactionStatus.Active} - def isCompleted = synchronized { status == TransactionStatus.Completed } + def isCompleted = synchronized {status == TransactionStatus.Completed} - def isAborted = synchronized { status == TransactionStatus.Aborted } + def isAborted = synchronized {status == TransactionStatus.Aborted} // --- internal methods --------- @@ -259,13 +277,13 @@ object Transaction extends TransactionManagement { private def ensureIsActiveOrAborted = if (!(status == TransactionStatus.Active || status == TransactionStatus.Aborted)) - throw new IllegalStateException( - "Expected ACTIVE or ABORTED transaction - current status [" + status + "]: " + toString) + throw new IllegalStateException( + "Expected ACTIVE or ABORTED transaction - current status [" + status + "]: " + toString) private def ensureIsActiveOrNew = if (!(status == TransactionStatus.Active || status == TransactionStatus.New)) - throw new IllegalStateException( - "Expected ACTIVE or NEW transaction - current status [" + status + "]: " + toString) + throw new IllegalStateException( + "Expected ACTIVE or NEW transaction - current status [" + status + "]: " + toString) // For reinitialize transaction after sending it over the wire private[akka] def reinit = synchronized { @@ -277,14 +295,14 @@ object Transaction extends TransactionManagement { } override def equals(that: Any): Boolean = synchronized { - that != null && - that.isInstanceOf[Transaction] && - that.asInstanceOf[Transaction].id == this.id + that != null && + that.isInstanceOf[Transaction] && + that.asInstanceOf[Transaction].id == this.id } - - override def hashCode(): Int = synchronized { id.toInt } - - override def toString(): String = synchronized { "Transaction[" + id + ", " + status + "]" } + + override def hashCode(): Int = synchronized {id.toInt} + + override def toString(): String = synchronized {"Transaction[" + id + ", " + status + "]"} } /** diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index 2dd7ed9c79..1f2ede3024 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -26,7 +26,7 @@ object TransactionManagement extends TransactionManagement { def isTransactionalityEnabled = TRANSACTION_ENABLED.get def disableTransactions = TRANSACTION_ENABLED.set(false) - private[akka] val currentTransaction: ThreadLocal[Option[Transaction]] = new ThreadLocal[Option[Transaction]]() { + private[akka] val currentTransaction = new ThreadLocal[Option[Transaction]]() { override protected def initialValue: Option[Transaction] = None } } @@ -52,6 +52,8 @@ trait TransactionManagement extends Logging { private[akka] def isTransactionInScope = currentTransaction.get.isDefined + private[akka] def isTransactionTopLevel = if (isTransactionInScope) getTransactionInScope.isTopLevel + private[akka] def incrementTransaction = if (isTransactionInScope) getTransactionInScope.increment private[akka] def decrementTransaction = if (isTransactionInScope) getTransactionInScope.decrement diff --git a/akka-core/src/main/scala/stm/TransactionalState.scala b/akka-core/src/main/scala/stm/TransactionalState.scala index 6003a89f89..195b134271 100644 --- a/akka-core/src/main/scala/stm/TransactionalState.scala +++ b/akka-core/src/main/scala/stm/TransactionalState.scala @@ -8,8 +8,7 @@ import se.scalablesolutions.akka.stm.Transaction.atomic import se.scalablesolutions.akka.stm.NoTransactionInScopeException import se.scalablesolutions.akka.collection._ import se.scalablesolutions.akka.util.UUID - -import org.multiverse.datastructures.refs.manual.Ref; +import org.multiverse.stms.alpha.AlphaRef /** * Example Scala usage: @@ -78,7 +77,7 @@ class TransactionalRef[T] extends Transactional { implicit val txInitName = "TransactionalRef:Init" val uuid = UUID.newUuid.toString - private[this] val ref: Ref[T] = atomic { new Ref } + private[this] val ref: AlphaRef[T] = atomic { new AlphaRef } def swap(elem: T) = { ensureIsInTransaction diff --git a/akka-util-java/pom.xml b/akka-util-java/pom.xml index 3daa008792..6db055b223 100644 --- a/akka-util-java/pom.xml +++ b/akka-util-java/pom.xml @@ -24,34 +24,6 @@ protobuf-java 2.2.0
- - org.multiverse - multiverse-alpha - 0.4-SNAPSHOT - jar-with-dependencies - - - org.multiverse - multiverse-core - - - asm - asm-tree - - - asm - asm-analysis - - - asm - asm-commons - - - asm - asm-util - - - diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/stm/AtomicTemplate.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/stm/AtomicTemplate.java deleted file mode 100644 index a693ff1248..0000000000 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/stm/AtomicTemplate.java +++ /dev/null @@ -1,341 +0,0 @@ -package se.scalablesolutions.akka.stm; - -import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; -import org.multiverse.api.Stm; -import static org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction; -import static org.multiverse.api.ThreadLocalTransaction.setThreadLocalTransaction; -import org.multiverse.api.Transaction; -import org.multiverse.api.TransactionStatus; -import org.multiverse.api.exceptions.CommitFailureException; -import org.multiverse.api.exceptions.LoadException; -import org.multiverse.api.exceptions.RetryError; -import org.multiverse.api.exceptions.TooManyRetriesException; -import org.multiverse.templates.AbortedException; -import org.multiverse.utils.latches.CheapLatch; -import org.multiverse.utils.latches.Latch; - -import static java.lang.String.format; -import java.util.logging.Logger; - -/** - * A Template that handles the boilerplate code for transactions. A transaction will be placed if none is available - * around a section and if all goes right, commits at the end. - *

- * example: - *

- * new AtomicTemplate(){
- *    Object execute(Transaction t){
- *        queue.push(1);
- *        return null;
- *    }
- * }.execute();
- * 
- *

- * It could also be that the transaction is retried (e.g. caused by optimistic locking failures). This is also a task - * for template. In the future this retry behavior will be customizable. - *

- * If a transaction already is available on the TransactionThreadLocal, no new transaction is started and essentially - * the whole AtomicTemplate is ignored. - *

- * If no transaction is available on the TransactionThreadLocal, a new one will be created and used during the execution - * of the AtomicTemplate and will be removed once the AtomicTemplate finishes. - *

- * All uncaught throwable's lead to a rollback of the transaction. - *

- * AtomicTemplates are not thread-safe to use. - *

- * AtomicTemplates can completely work without threadlocals. See the {@link AtomicTemplate#AtomicTemplate(org.multiverse.api.Stm - * ,String, boolean, boolean, int)} for more information. - * - * @author Peter Veentjer - */ -public abstract class AtomicTemplate { - - private final static Logger logger = Logger.getLogger(AtomicTemplate.class.getName()); - - private final Stm stm; - private final boolean ignoreThreadLocalTransaction; - private final int retryCount; - private final boolean readonly; - private int attemptCount; - private final String familyName; - - /** - * Creates a new AtomicTemplate that uses the STM stored in the GlobalStm and works the the {@link - * org.multiverse.utils.ThreadLocalTransaction}. - */ - public AtomicTemplate() { - this(getGlobalStmInstance()); - } - - public AtomicTemplate(boolean readonly) { - this(getGlobalStmInstance(), null, false, readonly, Integer.MAX_VALUE); - } - - /** - * Creates a new AtomicTemplate using the provided stm. The transaction used is stores/retrieved from the {@link - * org.multiverse.utils.ThreadLocalTransaction}. - * - * @param stm the stm to use for transactions. - * @throws NullPointerException if stm is null. - */ - public AtomicTemplate(Stm stm) { - this(stm, null, false, false, Integer.MAX_VALUE); - } - - public AtomicTemplate(String familyName, boolean readonly, int retryCount) { - this(getGlobalStmInstance(), familyName, false, readonly, retryCount); - } - - /** - * Creates a new AtomicTemplate that uses the provided STM. This method is provided to make Multiverse easy to - * integrate with environment that don't want to depend on threadlocals. - * - * @param stm the stm to use for transactions. - * @param ignoreThreadLocalTransaction true if this Template should completely ignore the ThreadLocalTransaction. - * This is useful for using the AtomicTemplate in other environments that don't - * want to depend on threadlocals but do want to use the AtomicTemplate. - * @throws NullPointerException if stm is null. - */ - public AtomicTemplate(Stm stm, String familyName, boolean ignoreThreadLocalTransaction, boolean readonly, - int retryCount) { - if (stm == null) { - throw new NullPointerException(); - } - if (retryCount < 0) { - throw new IllegalArgumentException(); - } - this.stm = stm; - this.ignoreThreadLocalTransaction = ignoreThreadLocalTransaction; - this.readonly = readonly; - this.retryCount = retryCount; - this.familyName = familyName; - } - - public String getFamilyName() { - return familyName; - } - - /** - * Returns the current attempt. Value will always be larger than zero and increases everytime the transaction needs - * to be retried. - * - * @return the current attempt count. - */ - public final int getAttemptCount() { - return attemptCount; - } - - /** - * Returns the number of retries that this AtomicTemplate is allowed to do. The returned value will always be equal - * or larger than 0. - * - * @return the number of retries. - */ - public final int getRetryCount() { - return retryCount; - } - - /** - * Returns the {@link Stm} used by this AtomicTemplate to execute transactions on. - * - * @return the Stm used by this AtomicTemplate. - */ - public final Stm getStm() { - return stm; - } - - /** - * Check if this AtomicTemplate ignores the ThreadLocalTransaction. - * - * @return true if this AtomicTemplate ignores the ThreadLocalTransaction, false otherwise. - */ - public final boolean isIgnoreThreadLocalTransaction() { - return ignoreThreadLocalTransaction; - } - - /** - * Checks if this AtomicTemplate executes readonly transactions. - * - * @return true if it executes readonly transactions, false otherwise. - */ - public final boolean isReadonly() { - return readonly; - } - - /** - * This is the method can be overridden to do pre-start tasks. - */ - public void preStart() { - } - - /** - * This is the method can be overridden to do post-start tasks. - * - * @param t the transaction used for this execution. - */ - public void postStart(Transaction t) { - } - - /** - * This is the method can be overridden to do pre-commit tasks. - */ - public void preCommit() { - } - - /** - * This is the method can be overridden to do post-commit tasks. - */ - public void postCommit() { - } - - /** - * This is the method that needs to be implemented. - * - * @param t the transaction used for this execution. - * @return the result of the execution. - * - * @throws Exception the Exception thrown - */ - public abstract E execute(Transaction t) throws Exception; - - /** - * Executes the template. - * - * @return the result of the {@link #execute(org.multiverse.api.Transaction)} method. - * - * @throws InvisibleCheckedException if a checked exception was thrown while executing the {@link - * #execute(org.multiverse.api.Transaction)} method. - * @throws AbortedException if the exception was explicitly aborted. - * @throws TooManyRetriesException if the template retried the transaction too many times. The cause of the last - * failure (also an exception) is included as cause. So you have some idea where - * to look for problems - */ - public final E execute() { - try { - return executeChecked(); - } catch (Exception ex) { - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } else { - throw new AtomicTemplate.InvisibleCheckedException(ex); - } - } - } - - /** - * Executes the Template and rethrows the checked exception instead of wrapping it in a InvisibleCheckedException. - * - * @return the result - * - * @throws Exception the Exception thrown inside the {@link #execute(org.multiverse.api.Transaction)} - * method. - * @throws AbortedException if the exception was explicitly aborted. - * @throws TooManyRetriesException if the template retried the transaction too many times. The cause of the last - * failure (also an exception) is included as cause. So you have some idea where to - * look for problems - */ - public final E executeChecked() throws Exception { - preStart(); - Transaction t = getTransaction(); - if (noUsableTransaction(t)) { - t = startTransaction(); - setTransaction(t); - postStart(t); - try { - attemptCount = 1; - Exception lastRetryCause = null; - while (attemptCount - 1 <= retryCount) { - boolean abort = true; - boolean reset = false; - try { - E result = execute(t); - if (t.getStatus().equals(TransactionStatus.aborted)) { - String msg = format("Transaction with familyname %s is aborted", t.getFamilyName()); - throw new AbortedException(msg); - } - preCommit(); - t.commit(); - abort = false; - reset = false; - postCommit(); - return result; - } catch (RetryError e) { - Latch latch = new CheapLatch(); - t.abortAndRegisterRetryLatch(latch); - latch.awaitUninterruptible(); - //since the abort is already done, no need to do it again. - abort = false; - } catch (CommitFailureException ex) { - lastRetryCause = ex; - reset = true; - //ignore, just retry the transaction - } catch (LoadException ex) { - lastRetryCause = ex; - reset = true; - //ignore, just retry the transaction - } finally { - if (abort) { - t.abort(); - if (reset) { - t = t.abortAndReturnRestarted(); - setTransaction(t); - } - } - } - attemptCount++; - } - - throw new TooManyRetriesException("Too many retries", lastRetryCause); - } finally { - setTransaction(null); - } - } else { - return execute(t); - } - } - - private Transaction startTransaction() { - return readonly ? stm.startReadOnlyTransaction(familyName) : stm.startUpdateTransaction(familyName); - } - - private boolean noUsableTransaction(Transaction t) { - return t == null || t.getStatus() != TransactionStatus.active; - } - - /** - * Gets the current Transaction stored in the TransactionThreadLocal. - *

- * If the ignoreThreadLocalTransaction is set, the threadlocal stuff is completeley ignored. - * - * @return the found transaction, or null if none is found. - */ - private Transaction getTransaction() { - return ignoreThreadLocalTransaction ? null : getThreadLocalTransaction(); - } - - /** - * Stores the transaction in the TransactionThreadLocal. - *

- * This call is ignored if the ignoreThreadLocalTransaction is true. - * - * @param t the transaction to set (is allowed to be null). - */ - private void setTransaction(Transaction t) { - if (!ignoreThreadLocalTransaction) { - setThreadLocalTransaction(t); - } - } - - public static class InvisibleCheckedException extends RuntimeException { - - public InvisibleCheckedException(Exception cause) { - super(cause); - } - - @Override - public Exception getCause() { - return (Exception) super.getCause(); - } - } -} diff --git a/pom.xml b/pom.xml index 3cfc2839a8..d2885abf61 100644 --- a/pom.xml +++ b/pom.xml @@ -14,25 +14,27 @@ Akka implements a unique hybrid of: - * Actors , which gives you: - * Simple and high-level abstractions for concurrency and parallelism. - * Asynchronous, non-blocking and highly performant event-driven programming model. - * Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM). - * Supervision hierarchies with let-it-crash semantics. For writing highly fault-tolerant systems that never stop, systems that self-heal. - * Software Transactional Memory (STM). (Distributed transactions coming soon). - * Transactors: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic rollback and retry. - * Remoting: highly performant distributed actors with remote supervision and error management. - * Cluster membership management. + * Actors , which gives you: + * Simple and high-level abstractions for concurrency and parallelism. + * Asynchronous, non-blocking and highly performant event-driven programming model. + * Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM). + * Supervision hierarchies with let-it-crash semantics. For writing highly fault-tolerant systems that never stop, + systems that self-heal. + * Software Transactional Memory (STM). (Distributed transactions coming soon). + * Transactors: combine actors and STM into transactional actors. Allows you to compose atomic message flows with + automatic rollback and retry. + * Remoting: highly performant distributed actors with remote supervision and error management. + * Cluster membership management. Akka also has a set of add-on modules: - * Persistence: A set of pluggable back-end storage modules that works in sync with the STM. - * Cassandra distributed and highly scalable database. - * MongoDB document database. - * Redis data structures database (upcoming) - * REST (JAX-RS): Expose actors as REST services. - * Comet: Expose actors as Comet services. - * Security: Digest and Kerberos based security. - * Microkernel: Run Akka as a stand-alone kernel. + * Persistence: A set of pluggable back-end storage modules that works in sync with the STM. + * Cassandra distributed and highly scalable database. + * MongoDB document database. + * Redis data structures database (upcoming) + * REST (JAX-RS): Expose actors as REST services. + * Comet: Expose actors as Comet services. + * Security: Digest and Kerberos based security. + * Microkernel: Run Akka as a stand-alone kernel. @@ -155,15 +157,9 @@ http://www.lag.net/repo - multiverse-releases - http://multiverse.googlecode.com/svn/maven-repository/releases - - false - - - - multiverse-snaphosts - http://multiverse.googlecode.com/svn/maven-repository/snapshots + repository.codehaus.org + Codehaus Maven Repository + http://repository.codehaus.org maven2-repository.dev.java.net @@ -258,7 +254,7 @@ src/main/scala src/test/scala - + org.apache.maven.plugins maven-enforcer-plugin 1.0-beta-1 @@ -276,7 +272,7 @@ - ${env.AKKA_HOME}/embedded-repo + ${env.AKKA_HOME}/embedded-repo From 4efb212841fc28c5da63f82d956dc86ece4405a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Tue, 23 Feb 2010 11:15:37 +0100 Subject: [PATCH 03/81] renamed actor api --- akka-core/src/main/scala/actor/Actor.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index a6d9231b10..d6185b65a3 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -98,9 +98,7 @@ object Actor extends Logging { * The actor is started when created. * Example: *

-   * import Actor._
-   *
-   * val a = actor  {
+   * val a = Actor.init  {
    *   ... // init stuff
    * } receive  {
    *   case msg => ... // handle message
@@ -108,7 +106,7 @@ object Actor extends Logging {
    * 
* */ - def actor[A](body: => Unit) = { + def init[A](body: => Unit) = { def handler[A](body: => Unit) = new { def receive(handler: PartialFunction[Any, Unit]) = new Actor() { start From a86fc10968deca5bab778f5e3d0bcbf0c12ddade Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 25 Feb 2010 17:19:50 +0100 Subject: [PATCH 04/81] initial camel integration (early-access, see also http://doc.akkasource.org/Camel) --- akka-camel/pom.xml | 51 ++++++ .../services/org/apache/camel/component/actor | 1 + akka-camel/src/main/scala/CamelConsumer.scala | 23 +++ .../main/scala/component/ActorComponent.scala | 166 ++++++++++++++++++ .../scala/service/CamelContextManager.scala | 23 +++ .../src/main/scala/service/CamelService.scala | 99 +++++++++++ .../scala/component/ActorComponentTest.scala | 57 ++++++ .../test/scala/service/CamelServiceTest.scala | 107 +++++++++++ akka-core/src/test/scala/SerializerTest.scala | 6 +- akka-kernel/pom.xml | 8 + akka-kernel/src/main/scala/Kernel.scala | 3 +- akka-samples/akka-sample-camel/pom.xml | 39 ++++ .../src/main/scala/Boot.scala | 36 ++++ .../src/main/scala/Consumer1.scala | 20 +++ .../src/main/scala/Consumer2.scala | 18 ++ akka-samples/pom.xml | 6 + .../akka/annotation/consume.java | 18 ++ config/akka-reference.conf | 9 +- pom.xml | 1 + 19 files changed, 684 insertions(+), 7 deletions(-) create mode 100644 akka-camel/pom.xml create mode 100644 akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/actor create mode 100644 akka-camel/src/main/scala/CamelConsumer.scala create mode 100644 akka-camel/src/main/scala/component/ActorComponent.scala create mode 100644 akka-camel/src/main/scala/service/CamelContextManager.scala create mode 100644 akka-camel/src/main/scala/service/CamelService.scala create mode 100644 akka-camel/src/test/scala/component/ActorComponentTest.scala create mode 100644 akka-camel/src/test/scala/service/CamelServiceTest.scala create mode 100644 akka-samples/akka-sample-camel/pom.xml create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Boot.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala create mode 100644 akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java diff --git a/akka-camel/pom.xml b/akka-camel/pom.xml new file mode 100644 index 0000000000..bc42439e84 --- /dev/null +++ b/akka-camel/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + akka-camel + Akka Camel Module + + jar + + + akka + se.scalablesolutions.akka + 0.7-SNAPSHOT + + + + + + akka-core + ${project.groupId} + ${project.version} + + + org.apache.camel + camel-core + 2.2.0 + + + org.apache.camel + camel-jetty + 2.2.0 + + + + + org.scalatest + scalatest + 1.0 + test + + + junit + junit + 4.5 + test + + + + \ No newline at end of file diff --git a/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/actor b/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/actor new file mode 100644 index 0000000000..a2141db8a9 --- /dev/null +++ b/akka-camel/src/main/resources/META-INF/services/org/apache/camel/component/actor @@ -0,0 +1 @@ +class=se.scalablesolutions.akka.camel.component.ActorComponent \ No newline at end of file diff --git a/akka-camel/src/main/scala/CamelConsumer.scala b/akka-camel/src/main/scala/CamelConsumer.scala new file mode 100644 index 0000000000..f3518e03f6 --- /dev/null +++ b/akka-camel/src/main/scala/CamelConsumer.scala @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel + +import se.scalablesolutions.akka.actor.Actor + +/** + * Mixed in by Actor subclasses to be Camel endpoint consumers. + * + * @author Martin Krasser + */ +trait CamelConsumer { + + self: Actor => + + /** + * Returns the Camel endpoint URI to consume messages from. + */ + def endpointUri: String + +} \ No newline at end of file diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala new file mode 100644 index 0000000000..4c99bfd809 --- /dev/null +++ b/akka-camel/src/main/scala/component/ActorComponent.scala @@ -0,0 +1,166 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel.component + +import java.lang.{RuntimeException, String} +import java.util.{Map => JavaMap} + +import org.apache.camel.{Exchange, Consumer, Processor} +import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent} + +import se.scalablesolutions.akka.actor.{ActorRegistry, Actor} + +/** + * Camel component for interacting with actors. + * + * @see se.scalablesolutions.akka.camel.component.ActorEndpoint + * @see se.scalablesolutions.akka.camel.component.ActorProducer + * + * @author Martin Krasser + */ +class ActorComponent extends DefaultComponent { + + def createEndpoint(uri: String, remaining: String, parameters: JavaMap[String, Object]): ActorEndpoint = { + val idAndUuid = idAndUuidPair(remaining) + new ActorEndpoint(uri, this, idAndUuid._1, idAndUuid._2) + } + + private def idAndUuidPair(remaining: String): Tuple2[String, Option[String]] = { + remaining split "/" toList match { + case id :: Nil => (id, None) + case id :: uuid :: Nil => (id, Some(uuid)) + case _ => throw new IllegalArgumentException( + "invalid path format: %s - should be [/]" format remaining) + } + } + +} + +/** + * Camel endpoint for interacting with actors. An actor can be addressed by its + * Actor.id or by an Actor.id - Actor.uuid + * combination. The URI format is actor://[/]. + * + * @see se.scalablesolutions.akka.camel.component.ActorComponent + * @see se.scalablesolutions.akka.camel.component.ActorProducer + + * @author Martin Krasser + */ +class ActorEndpoint(uri: String, comp: ActorComponent, val id: String, val uuid: Option[String]) extends DefaultEndpoint(uri, comp) { + + // TODO: clarify uuid details + // - do they change after persist/restore + // - what about remote actors and uuids + + /** + * @throws UnsupportedOperationException + */ + def createConsumer(processor: Processor): Consumer = + throw new UnsupportedOperationException("actor consumer not supported yet") + + def createProducer: ActorProducer = new ActorProducer(this) + + def isSingleton: Boolean = true + +} + +/** + * Sends the in-message of an exchange to an actor. If the exchange pattern is out-capable, + * the producer waits for a reply (using the !! operator), otherwise the ! operator is used + * for sending the message. Asynchronous communication is not implemented yet but will be + * added for Camel components that support the Camel Async API (like the jetty component that + * makes use of Jetty continuations). + * + * @see se.scalablesolutions.akka.camel.component.ActorComponent + * @see se.scalablesolutions.akka.camel.component.ActorEndpoint + * + * @author Martin Krasser + */ +class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { + + implicit val sender = Some(Sender) + + def process(exchange: Exchange) { + val actor = target getOrElse (throw new ActorNotRegisteredException(ep.id, ep.uuid)) + if (exchange.getPattern.isOutCapable) + processInOut(exchange, actor) + else + processInOnly(exchange, actor) + } + + override def start { + super.start + } + + protected def receive = { + throw new UnsupportedOperationException + } + + protected def processInOnly(exchange: Exchange, actor: Actor) { + actor ! exchange.getIn + } + + protected def processInOut(exchange: Exchange, actor: Actor) { + val outmsg = exchange.getOut + // TODO: make timeout configurable + // TODO: send immutable message + // TODO: support asynchronous communication + // - jetty component: jetty continuations + // - file component: completion callbacks + val result: Any = actor !! exchange.getIn + + result match { + case Some((body, headers:Map[String, Any])) => { + outmsg.setBody(body) + for (header <- headers) + outmsg.getHeaders.put(header._1, header._2.asInstanceOf[AnyRef]) + } + case Some(body) => outmsg.setBody(body) + } + } + + private def target: Option[Actor] = { + ActorRegistry.actorsFor(ep.id) match { + case actor :: Nil if targetMatchesUuid(actor) => Some(actor) + case Nil => None + case actors => actors find (targetMatchesUuid _) + } + } + + private def targetMatchesUuid(target: Actor): Boolean = + // if ep.uuid is not defined always return true + target.uuid == (ep.uuid getOrElse target.uuid) + +} + +/** + * Generic message sender used by ActorProducer. + * + * @author Martin Krasser + */ +private[component] object Sender extends Actor { + + start + + /** + * Ignores any message. + */ + protected def receive = { + case _ => { /* ignore any reply */ } + } + +} + +/** + * Thrown to indicate that an actor referenced by an endpoint URI cannot be + * found in the ActorRegistry. + * + * @author Martin Krasser + */ +class ActorNotRegisteredException(name: String, uuid: Option[String]) extends RuntimeException { + + override def getMessage = "actor(id=%s,uuid=%s) not registered" format (name, uuid getOrElse "") + +} \ No newline at end of file diff --git a/akka-camel/src/main/scala/service/CamelContextManager.scala b/akka-camel/src/main/scala/service/CamelContextManager.scala new file mode 100644 index 0000000000..a6f84c158c --- /dev/null +++ b/akka-camel/src/main/scala/service/CamelContextManager.scala @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel.service + +import org.apache.camel.CamelContext +import org.apache.camel.impl.DefaultCamelContext + +/** + * Manages the CamelContext used by CamelService. + * + * @author Martin Krasser + */ +object CamelContextManager { + + /** + * The CamelContext used by CamelService. Can be modified by applications prior to + * loading the CamelService. + */ + var context: CamelContext = new DefaultCamelContext + +} \ No newline at end of file diff --git a/akka-camel/src/main/scala/service/CamelService.scala b/akka-camel/src/main/scala/service/CamelService.scala new file mode 100644 index 0000000000..2811ab88fe --- /dev/null +++ b/akka-camel/src/main/scala/service/CamelService.scala @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel.service + +import java.io.InputStream + +import org.apache.camel.builder.RouteBuilder + +import se.scalablesolutions.akka.actor.{Actor, ActorRegistry} +import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.camel.CamelConsumer +import se.scalablesolutions.akka.util.{Bootable, Logging} + +/** + * Started by the Kernel to expose actors as Camel endpoints. + * + * @see CamelRouteBuilder + * + * @author Martin Krasser + */ +trait CamelService extends Bootable with Logging { + + import CamelContextManager.context + + abstract override def onLoad = { + super.onLoad + context.addRoutes(new CamelRouteBuilder) + context.setStreamCaching(true) + context.start + log.info("Camel context started") + } + + abstract override def onUnload = { + super.onUnload + context.stop + log.info("Camel context stopped") + } + +} + +/** + * Generic route builder that searches the registry for actors that are + * either annotated with @se.scalablesolutions.akka.annotation.consume or + * mixed in se.scalablesolutions.akka.camel.CamelConsumer and exposes them + * as Camel endpoints. + * + * @author Martin Krasser + */ +class CamelRouteBuilder extends RouteBuilder with Logging { + + def configure = { + val actors = ActorRegistry.actors + + // + // TODO: resolve/clarify issues with ActorRegistry + // - custom Actor.id ignored + // - actor de-registration issues + // - multiple registration with same id/uuid possible + // + + // TODO: avoid redundant registrations + actors.filter(isConsumeAnnotated _).foreach { actor: Actor => + val fromUri = actor.getClass.getAnnotation(classOf[consume]).value() + configure(fromUri, "actor://%s" format actor.id) + log.debug("registered actor (id=%s) for consuming messages from %s " + format (actor.id, fromUri)) + } + + // TODO: avoid redundant registrations + actors.filter(isConsumerInstance _).foreach { actor: Actor => + val fromUri = actor.asInstanceOf[CamelConsumer].endpointUri + configure(fromUri, "actor://%s/%s" format (actor.id, actor.uuid)) + log.debug("registered actor (id=%s, uuid=%s) for consuming messages from %s " + format (actor.id, actor.uuid, fromUri)) + } + } + + private def configure(fromUri: String, toUri: String) { + val schema = fromUri take fromUri.indexOf(":") // e.g. "http" from "http://whatever/..." + bodyConversions.get(schema) match { + case Some(clazz) => from(fromUri).convertBodyTo(clazz).to(toUri) + case None => from(fromUri).to(toUri) + } + } + + // TODO: make conversions configurable + private def bodyConversions = Map( + "file" -> classOf[InputStream] + ) + + private def isConsumeAnnotated(actor: Actor) = + actor.getClass.getAnnotation(classOf[consume]) ne null + + private def isConsumerInstance(actor: Actor) = + actor.isInstanceOf[CamelConsumer] + +} diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala new file mode 100644 index 0000000000..3a06d483be --- /dev/null +++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala @@ -0,0 +1,57 @@ +package se.scalablesolutions.akka.camel.component + +import org.apache.camel.{Message, RuntimeCamelException} +import org.apache.camel.impl.{SimpleRegistry, DefaultCamelContext} +import org.junit._ +import org.junit.Assert._ +import org.scalatest.junit.JUnitSuite + +import se.scalablesolutions.akka.actor.Actor + +/** + * @author Martin Krasser + */ +class ActorComponentTest extends JUnitSuite { + + import ActorComponentTestSetup._ + + val actor = ActorComponentTestActor.start + + @Test + def testMatchIdOnly() { + val result = template.requestBody("actor:%s" format actor.id, "Martin") + assertEquals("Hello Martin", result) + } + + @Test + def testMatchIdAndUuid() { + val result = template.requestBody("actor:%s/%s" format (actor.id, actor.uuid), "Martin") + assertEquals("Hello Martin", result) + } + + @Test + def testMatchIdButNotUuid() { + intercept[RuntimeCamelException] { + template.requestBody("actor:%s/%s" format (actor.id, "wrong"), "Martin") + } + } + +} + +object ActorComponentTestActor extends Actor { + + protected def receive = { + case msg: Message => reply("Hello %s" format msg.getBody) + } + +} + +object ActorComponentTestSetup { + + val context = new DefaultCamelContext(new SimpleRegistry) + val template = context.createProducerTemplate + + context.start + template.start + +} \ No newline at end of file diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala new file mode 100644 index 0000000000..8cce9ec2bd --- /dev/null +++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala @@ -0,0 +1,107 @@ +package se.scalablesolutions.akka.camel.service + +import org.apache.camel.Message +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.impl.DefaultCamelContext +import org.junit.Test +import org.junit.Assert._ +import org.scalatest.junit.JUnitSuite + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.camel.CamelConsumer + + +/** + * @author Martin Krasser + */ +class CamelServiceTest extends JUnitSuite { + + import CamelServiceTestSetup._ + + @Test + def testActor1() { + val result = template.requestBody("direct:actor1", "Martin") + assertEquals("Hello Martin (actor1)", result) + } + + @Test + def testActor2() { + val result = template.requestBody("direct:actor2", "Martin") + assertEquals("Hello Martin (actor2)", result) + } + + @Test + def testActor3() { + val result = template.requestBody("direct:actor3", "Martin") + assertEquals("Hello Tester (actor3)", result) + } + +} + +class TestActor1 extends Actor with CamelConsumer { + + def endpointUri = "direct:actor1" + + protected def receive = { + case msg: Message => reply("Hello %s (actor1)" format msg.getBody) + } + +} + +@consume("direct:actor2") +class TestActor2 extends Actor { + + protected def receive = { + case msg: Message => reply("Hello %s (actor2)" format msg.getBody) + } + +} + +class TestActor3 extends Actor { + + protected def receive = { + case msg: Message => reply("Hello %s (actor3)" format msg.getBody) + } + +} + +class TestBuilder extends RouteBuilder { + + def configure { + val actorUri = "actor://%s" format classOf[TestActor3].getName + from("direct:actor3").transform(constant("Tester")).to(actorUri) + } + +} + +object CamelServiceTestSetup extends CamelService { + + import CamelContextManager.context + + // use a custom camel context + context = new DefaultCamelContext + + val template = context.createProducerTemplate + var loaded = false + + onLoad + + override def onLoad = { + if (!loaded) { + // use a custom camel context + context.addRoutes(new TestBuilder) + // register test actors + new TestActor1().start + new TestActor2().start + new TestActor3().start + // start Camel service + super.onLoad + + template.start + loaded = true + } + } + +} + diff --git a/akka-core/src/test/scala/SerializerTest.scala b/akka-core/src/test/scala/SerializerTest.scala index e11e83a2f5..889dea4ba8 100644 --- a/akka-core/src/test/scala/SerializerTest.scala +++ b/akka-core/src/test/scala/SerializerTest.scala @@ -3,7 +3,7 @@ package se.scalablesolutions.akka.serialization import junit.framework.TestCase import org.scalatest.junit.JUnitSuite -import org.junit.{Test, Before, After} +import org.junit.{Test, Before, After, Ignore} import scala.reflect.BeanInfo @BeanInfo @@ -18,7 +18,7 @@ case class MyMessage(val id: String, val value: Tuple2[String, Int]) { class SerializerTest extends JUnitSuite { - @Test + @Test @Ignore // TODO: resolve test failure def shouldSerializeString = { val f = Foo("debasish") val json = Serializer.ScalaJSON.out(f) @@ -27,7 +27,7 @@ class SerializerTest extends JUnitSuite { assert(fo == f) } - @Test + @Test @Ignore // TODO: resolve test failure def shouldSerializeTuple2 = { val message = MyMessage("id", ("hello", 34)) val json = Serializer.ScalaJSON.out(message) diff --git a/akka-kernel/pom.xml b/akka-kernel/pom.xml index 4b1d114d45..1d578023fd 100644 --- a/akka-kernel/pom.xml +++ b/akka-kernel/pom.xml @@ -51,6 +51,11 @@ ${project.groupId} ${project.version} + + akka-camel + ${project.groupId} + ${project.version} + akka-cluster-jgroups ${project.groupId} @@ -104,6 +109,9 @@ + + META-INF/services/org/apache/camel/TypeConverter + diff --git a/akka-kernel/src/main/scala/Kernel.scala b/akka-kernel/src/main/scala/Kernel.scala index f63a50a0a7..406c914577 100644 --- a/akka-kernel/src/main/scala/Kernel.scala +++ b/akka-kernel/src/main/scala/Kernel.scala @@ -4,6 +4,7 @@ package se.scalablesolutions.akka +import se.scalablesolutions.akka.camel.service.CamelService import se.scalablesolutions.akka.remote.BootableRemoteActorService import se.scalablesolutions.akka.actor.BootableActorLoaderService import se.scalablesolutions.akka.util.{Logging,Bootable} @@ -32,7 +33,7 @@ object Kernel extends Logging { /** * Boots up the Kernel with default bootables */ - def boot : Unit = boot(true, new BootableActorLoaderService with BootableRemoteActorService with BootableCometActorService) + def boot : Unit = boot(true, new BootableActorLoaderService with BootableRemoteActorService with BootableCometActorService with CamelService) /** * Boots up the Kernel. diff --git a/akka-samples/akka-sample-camel/pom.xml b/akka-samples/akka-sample-camel/pom.xml new file mode 100644 index 0000000000..95adba3149 --- /dev/null +++ b/akka-samples/akka-sample-camel/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + akka-sample-camel + Akka Camel Sample Module + + jar + + + akka-samples-parent + se.scalablesolutions.akka + 0.7-SNAPSHOT + + + + src/main/scala + + + maven-antrun-plugin + + + install + + + + + + + run + + + + + + + + diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala new file mode 100644 index 0000000000..0b3726c08b --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -0,0 +1,36 @@ +package sample.camel + +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.impl.DefaultCamelContext + +import se.scalablesolutions.akka.actor.SupervisorFactory +import se.scalablesolutions.akka.camel.service.CamelContextManager +import se.scalablesolutions.akka.config.ScalaConfig._ + +/** + * @author Martin Krasser + */ +class Boot { + + import CamelContextManager.context + + context = new DefaultCamelContext + context.addRoutes(new CustomRouteBuilder) + + val factory = SupervisorFactory( + SupervisorConfig( + RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), + Supervise(new Consumer1, LifeCycle(Permanent)) :: + Supervise(new Consumer2, LifeCycle(Permanent)) :: Nil)) + factory.newInstance.start + +} + +class CustomRouteBuilder extends RouteBuilder { + + def configure { + val actorUri = "actor:%s" format classOf[Consumer2].getName + from ("jetty:http://0.0.0.0:8877/camel/test2").to(actorUri) + } + +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala new file mode 100644 index 0000000000..fd9b38a3a9 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala @@ -0,0 +1,20 @@ +package sample.camel + +import org.apache.camel.Message + +import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.camel.CamelConsumer + +/** + * @author Martin Krasser + */ +class Consumer1 extends Actor with CamelConsumer with Logging { + + def endpointUri = "file:data/input" + + def receive = { + case msg: Message => log.info("received %s" format msg.getBody(classOf[String])) + } + +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala new file mode 100644 index 0000000000..aa9cd5e612 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala @@ -0,0 +1,18 @@ +package sample.camel + +import org.apache.camel.Message + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.annotation.consume + +/** + * @author Martin Krasser + */ +@consume("jetty:http://0.0.0.0:8877/camel/test1") +class Consumer2 extends Actor { + + def receive = { + case msg: Message => reply("Hello %s" format msg.getBody(classOf[String])) + } + +} \ No newline at end of file diff --git a/akka-samples/pom.xml b/akka-samples/pom.xml index ad94fc8aab..427ad3d665 100644 --- a/akka-samples/pom.xml +++ b/akka-samples/pom.xml @@ -19,6 +19,7 @@ akka-sample-security akka-sample-rest-scala akka-sample-rest-java + akka-sample-camel @@ -47,6 +48,11 @@ ${project.groupId} ${project.version} + + akka-camel + ${project.groupId} + ${project.version} + akka-security ${project.groupId} diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java new file mode 100644 index 0000000000..3f8ab9455a --- /dev/null +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface consume { + + public abstract String value(); + +} \ No newline at end of file diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 749b599e0b..48207e9966 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -19,9 +19,12 @@ # FQN to the class doing initial active object/actor # supervisor bootstrap, should be defined in default constructor - boot = ["sample.java.Boot", - "sample.scala.Boot", - "se.scalablesolutions.akka.security.samples.Boot"] + boot = ["sample.camel.Boot"] + + # Disable other boot configurations at the moment + #boot = ["sample.java.Boot", + # "sample.scala.Boot", + # "se.scalablesolutions.akka.security.samples.Boot"] timeout = 5000 # default timeout for future based invocations diff --git a/pom.xml b/pom.xml index 3cfc2839a8..dec242af2d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ akka-amqp akka-security akka-patterns + akka-camel akka-kernel akka-fun-test-java akka-samples From 2e81ac1f86d78fafa8e40220a8f7b7e89b04665b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Tue, 23 Feb 2010 19:49:01 +0100 Subject: [PATCH 05/81] Upgraded to Multiverse 0.4 and its 2PC CommitBarriers, all tests pass --- akka-core/pom.xml | 23 --- akka-core/src/main/scala/actor/Actor.scala | 81 ++++++--- .../src/main/scala/dispatch/Reactor.scala | 9 +- .../src/main/scala/stm/Transaction.scala | 158 ++++++++---------- .../scala/stm/TransactionManagement.scala | 75 ++++++--- .../main/scala/stm/TransactionalState.scala | 2 +- .../src/test/scala/InMemoryActorTest.scala | 28 ++-- akka-core/src/test/scala/ShutdownSpec.scala | 5 +- .../akka/api/InMemNestedStateTest.java | 3 +- akka-patterns/src/main/scala/Agent.scala | 24 +-- .../src/test/scala/ActorPatternsTest.scala | 6 +- akka-patterns/src/test/scala/AgentTest.scala | 27 +-- .../src/main/scala/Storage.scala | 22 +-- akka.iml | 13 +- config/akka-reference.conf | 6 +- pom.xml | 11 +- 16 files changed, 259 insertions(+), 234 deletions(-) diff --git a/akka-core/pom.xml b/akka-core/pom.xml index 5d58430aa6..d2116cd0f1 100644 --- a/akka-core/pom.xml +++ b/akka-core/pom.xml @@ -50,29 +50,6 @@ org.multiverse multiverse-alpha 0.4-SNAPSHOT - jar-with-dependencies - - - org.multiverse - multiverse-core - - - asm - asm-tree - - - asm - asm-analysis - - - asm - asm-commons - - - asm - asm-util - - org.scala-tools diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index d6185b65a3..2919cc5c71 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -10,7 +10,7 @@ import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, F import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.stm.Transaction._ import se.scalablesolutions.akka.stm.TransactionManagement._ -import se.scalablesolutions.akka.stm.{StmException, TransactionManagement} +import se.scalablesolutions.akka.stm.TransactionManagement import se.scalablesolutions.akka.remote.protobuf.RemoteProtocol.RemoteRequest import se.scalablesolutions.akka.remote.{RemoteProtocolBuilder, RemoteClient, RemoteRequestIdFactory} import se.scalablesolutions.akka.serialization.Serializer @@ -21,6 +21,7 @@ import org.multiverse.api.ThreadLocalTransaction._ import java.util.{Queue, HashSet} import java.util.concurrent.ConcurrentLinkedQueue import java.net.InetSocketAddress +import org.multiverse.commitbarriers.CountDownCommitBarrier /** * Implements the Transactor abstraction. E.g. a transactional actor. @@ -72,7 +73,7 @@ object Actor extends Logging { val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost") val PORT = config.getInt("akka.remote.server.port", 9999) - object Sender{ + object Sender { implicit val Self: Option[Actor] = None } @@ -193,7 +194,7 @@ object Actor extends Logging { */ trait Actor extends TransactionManagement { implicit protected val self: Option[Actor] = Some(this) - implicit protected val transactionFamily: String = this.getClass.getName + implicit protected val transactionFamilyName: String = this.getClass.getName // Only mutable for RemoteServer in order to maintain identity across nodes private[akka] var _uuid = UUID.newUuid.toString @@ -216,6 +217,7 @@ trait Actor extends TransactionManagement { private[akka] var _replyToAddress: Option[InetSocketAddress] = None private[akka] val _mailbox: Queue[MessageInvocation] = new ConcurrentLinkedQueue[MessageInvocation] + // ==================================== // protected fields // ==================================== @@ -333,8 +335,8 @@ trait Actor extends TransactionManagement { /** * User overridable callback/setting. * - * Partial function implementing the server logic. - * To be implemented by subclassing server. + * Partial function implementing the actor logic. + * To be implemented by subclassing actor. *

* Example code: *

@@ -782,6 +784,11 @@ trait Actor extends TransactionManagement {
   }
 
   protected[akka] def postMessageToMailbox(message: Any, sender: Option[Actor]): Unit = {
+    if (isTransactionSetInScope) {
+      log.trace("Adding transaction for %s with message [%s] to transaction set", toString, message)
+      getTransactionSetInScope.incParties
+    }
+
     if (_remoteAddress.isDefined) {
       val requestBuilder = RemoteRequest.newBuilder
           .setId(RemoteRequestIdFactory.nextId)
@@ -793,8 +800,7 @@ trait Actor extends TransactionManagement {
           .setIsEscaped(false)
       
       val id = registerSupervisorAsRemoteActor
-      if (id.isDefined)
-        requestBuilder.setSupervisorUuid(id.get)
+      if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
 
       // set the source fields used to reply back to the original sender
       // (i.e. not the remote proxy actor)
@@ -813,18 +819,24 @@ trait Actor extends TransactionManagement {
       RemoteProtocolBuilder.setMessage(message, requestBuilder)
       RemoteClient.clientFor(_remoteAddress.get).send(requestBuilder.build, None)
     } else {
-      val invocation = new MessageInvocation(this, message, None, sender, currentTransaction.get)
+      val invocation = new MessageInvocation(this, message, None, sender, transactionSet.get)
       if (_isEventBased) {
         _mailbox.add(invocation)
         if (_isSuspended) invocation.send
       } else invocation.send
     }
+    clearTransactionSet
   }
 
   protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout(
       message: Any, 
       timeout: Long,
       senderFuture: Option[CompletableFutureResult]): CompletableFutureResult = {
+    if (isTransactionSetInScope) {
+      log.trace("Adding transaction for %s with message [%s] to transaction set", toString, message)    
+      getTransactionSetInScope.incParties
+    }
+    
     if (_remoteAddress.isDefined) {
       val requestBuilder = RemoteRequest.newBuilder
           .setId(RemoteRequestIdFactory.nextId)
@@ -838,16 +850,18 @@ trait Actor extends TransactionManagement {
       val id = registerSupervisorAsRemoteActor
       if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
       val future = RemoteClient.clientFor(_remoteAddress.get).send(requestBuilder.build, senderFuture)
+      clearTransactionSet
       if (future.isDefined) future.get
       else throw new IllegalStateException("Expected a future from remote call to actor " + toString)
     } else {
       val future = if (senderFuture.isDefined) senderFuture.get
                    else new DefaultCompletableFutureResult(timeout)
-      val invocation = new MessageInvocation(this, message, Some(future), None, currentTransaction.get)
+      val invocation = new MessageInvocation(this, message, Some(future), None, transactionSet.get)
       if (_isEventBased) {
         _mailbox.add(invocation)
         invocation.send
       } else invocation.send
+      clearTransactionSet
       future
     }
   }
@@ -856,6 +870,7 @@ trait Actor extends TransactionManagement {
    * Callback for the dispatcher. E.g. single entry point to the user code and all protected[this] methods.
    */
   private[akka] def invoke(messageHandle: MessageInvocation) = synchronized {
+    log.trace("%s is invoked with message %s", toString, messageHandle)
     try {
       if (TransactionManagement.isTransactionalityEnabled) transactionalDispatch(messageHandle)
       else dispatch(messageHandle)
@@ -867,7 +882,7 @@ trait Actor extends TransactionManagement {
   }
 
   private def dispatch[T](messageHandle: MessageInvocation) = {
-    setTransaction(messageHandle.tx)
+    setTransactionSet(messageHandle.transactionSet)
 
     val message = messageHandle.message //serializeMessage(messageHandle.message)
     senderFuture = messageHandle.future
@@ -889,43 +904,55 @@ trait Actor extends TransactionManagement {
   }
 
   private def transactionalDispatch[T](messageHandle: MessageInvocation) = {
-    setTransaction(messageHandle.tx)
+    var topLevelTransaction = false
+    val txSet: Option[CountDownCommitBarrier] =
+      if (messageHandle.transactionSet.isDefined) messageHandle.transactionSet
+      else {
+        topLevelTransaction = true // FIXME create a new internal atomic block that can wait for X seconds if top level tx
+        if (isTransactionRequiresNew) {
+          log.trace("Creating a new transaction set (top-level transaction) \nfor actor %s \nwith message %s", toString, messageHandle)
+          Some(createNewTransactionSet)
+        } else None
+      }
+    setTransactionSet(txSet)
 
     val message = messageHandle.message //serializeMessage(messageHandle.message)
     senderFuture = messageHandle.future
     sender = messageHandle.sender
 
+    def clearTx = {
+      clearTransactionSet
+      clearTransaction
+    }
+
     def proceed = {
-      try {
-        incrementTransaction
-        if (base.isDefinedAt(message)) base(message) // invoke user actor's receive partial function
-        else throw new IllegalArgumentException(
-          "Actor " + toString + " could not process message [" + message + "]" +
-           "\n\tsince no matching 'case' clause in its 'receive' method could be found")
-      } finally {
-        decrementTransaction
-      }
+      if (base.isDefinedAt(message)) base(message) // invoke user actor's receive partial function
+      else throw new IllegalArgumentException(
+        toString + " could not process message [" + message + "]" +
+        "\n\tsince no matching 'case' clause in its 'receive' method could be found")
+      setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit
     }
 
     try {
-      if (isTransactionRequiresNew && !isTransactionInScope) {
-        //if (senderFuture.isEmpty) throw new StmException(
-        //  "Can't continue transaction in a one-way fire-forget message send" +
-        //  "\n\tE.g. using Actor '!' method or Active Object 'void' method" +
-        //  "\n\tPlease use the Actor '!!' method or Active Object method with non-void return type")
+      if (isTransactionRequiresNew) {
         atomic {
           proceed
         }
       } else proceed
     } catch {
+      case e: IllegalStateException => {}
       case e =>
+        // abort transaction set
+        if (isTransactionSetInScope) try { getTransactionSetInScope.abort } catch { case e: IllegalStateException => {} }
         Actor.log.error(e, "Exception when invoking \n\tactor [%s] \n\twith message [%s]", this, message)
+
         if (senderFuture.isDefined) senderFuture.get.completeWithException(this, e)
-        clearTransaction // need to clear currentTransaction before call to supervisor
+        clearTx  // need to clear currentTransaction before call to supervisor
+
         // FIXME to fix supervisor restart of remote actor for oneway calls, inject a supervisor proxy that can send notification back to client
         if (_supervisor.isDefined) _supervisor.get ! Exit(this, e)
     } finally {
-      clearTransaction
+      clearTx
     }
   }
 
diff --git a/akka-core/src/main/scala/dispatch/Reactor.scala b/akka-core/src/main/scala/dispatch/Reactor.scala
index f7bfa52215..b5d4d634f6 100644
--- a/akka-core/src/main/scala/dispatch/Reactor.scala
+++ b/akka-core/src/main/scala/dispatch/Reactor.scala
@@ -7,16 +7,17 @@ package se.scalablesolutions.akka.dispatch
 import java.util.List
 
 import se.scalablesolutions.akka.util.{HashCode, Logging}
-import se.scalablesolutions.akka.stm.Transaction
 import se.scalablesolutions.akka.actor.Actor
 
 import java.util.concurrent.ConcurrentHashMap
 
+import org.multiverse.commitbarriers.CountDownCommitBarrier
+
 final class MessageInvocation(val receiver: Actor,
                               val message: Any,
                               val future: Option[CompletableFutureResult],
                               val sender: Option[Actor],
-                              val tx: Option[Transaction]) {
+                              val transactionSet: Option[CountDownCommitBarrier]) {
   if (receiver eq null) throw new IllegalArgumentException("receiver is null")
 
   def invoke = receiver.invoke(this)
@@ -37,13 +38,13 @@ final class MessageInvocation(val receiver: Actor,
     that.asInstanceOf[MessageInvocation].message == message
   }
 
-  override def toString(): String = synchronized {
+  override def toString = synchronized {
     "MessageInvocation[" +
      "\n\tmessage = " + message +
      "\n\treceiver = " + receiver +
      "\n\tsender = " + sender +
      "\n\tfuture = " + future +
-     "\n\ttx = " + tx +
+     "\n\ttransactionSet = " + transactionSet +
      "\n]"
   }
 }
diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala
index d535f98433..133c292a6f 100644
--- a/akka-core/src/main/scala/stm/Transaction.scala
+++ b/akka-core/src/main/scala/stm/Transaction.scala
@@ -7,16 +7,18 @@ package se.scalablesolutions.akka.stm
 import java.util.concurrent.atomic.AtomicLong
 import java.util.concurrent.atomic.AtomicInteger
 
+import scala.collection.mutable.HashMap
+
 import se.scalablesolutions.akka.state.Committable
 import se.scalablesolutions.akka.util.Logging
 
 import org.multiverse.api.{Transaction => MultiverseTransaction}
 import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance
 import org.multiverse.api.ThreadLocalTransaction._
-import org.multiverse.commitbarriers.VetoCommitBarrier
-
-import scala.collection.mutable.HashMap
 import org.multiverse.templates.{TransactionTemplate, OrElseTemplate}
+import org.multiverse.utils.backoff.ExponentialBackoffPolicy
+import org.multiverse.stms.alpha.AlphaStm
+import java.util.concurrent.TimeUnit
 
 class NoTransactionInScopeException extends RuntimeException
 class TransactionRetryException(message: String) extends RuntimeException(message)
@@ -98,21 +100,42 @@ class TransactionRetryException(message: String) extends RuntimeException(messag
  */
 object Transaction extends TransactionManagement {
   val idFactory = new AtomicLong(-1L)
+/*
+  import AlphaStm._
+  private val defaultTxBuilder = new AlphaTransactionFactoryBuilder
+  defaultTxBuilder.setReadonly(false)
+  defaultTxBuilder.setInterruptible(INTERRUPTIBLE)
+  defaultTxBuilder.setMaxRetryCount(MAX_NR_OF_RETRIES)
+  defaultTxBuilder.setPreventWriteSkew(PREVENT_WRITE_SKEW)
+  defaultTxBuilder.setAutomaticReadTracking(AUTOMATIC_READ_TRACKING)
+  defaultTxBuilder.setSmartTxLengthSelector(SMART_TX_LENGTH_SELECTOR)
+  defaultTxBuilder.setBackoffPolicy(new ExponentialBackoffPolicy)
+  private val readOnlyTxBuilder = new AlphaStm.AlphaTransactionFactoryBuilder
+  readOnlyTxBuilder.setReadonly(true)
+  readOnlyTxBuilder.setInterruptible(INTERRUPTIBLE)
+  readOnlyTxBuilder.setMaxRetryCount(MAX_NR_OF_RETRIES)
+  readOnlyTxBuilder.setPreventWriteSkew(PREVENT_WRITE_SKEW)
+  readOnlyTxBuilder.setAutomaticReadTracking(AUTOMATIC_READ_TRACKING)
+  readOnlyTxBuilder.setSmartTxLengthSelector(SMART_TX_LENGTH_SELECTOR)
+  readOnlyTxBuilder.setBackoffPolicy(new ExponentialBackoffPolicy)
+  */
+  /**
+   * See ScalaDoc on class.
+   */
+  def map[T](f: => T)(implicit transactionFamilyName: String): T =
+    atomic {f}
 
   /**
    * See ScalaDoc on class.
    */
-  def map[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic {f(getTransactionInScope)}
+  def flatMap[T](f: => T)(implicit transactionFamilyName: String): T =
+    atomic {f}
 
   /**
    * See ScalaDoc on class.
    */
-  def flatMap[T](f: Transaction => T)(implicit transactionFamilyName: String): T = atomic {f(getTransactionInScope)}
-
-  /**
-   * See ScalaDoc on class.
-   */
-  def foreach(f: Transaction => Unit)(implicit transactionFamilyName: String): Unit = atomic {f(getTransactionInScope)}
+  def foreach(f: => Unit)(implicit transactionFamilyName: String): Unit =
+    atomic {f}
 
   /**
    * Creates a "pure" STM atomic transaction and by-passes all transactions hooks
@@ -127,82 +150,39 @@ object Transaction extends TransactionManagement {
    * See ScalaDoc on class.
    */
   def atomic[T](body: => T)(implicit transactionFamilyName: String): T = {
+    //    defaultTxBuilder.setFamilyName(transactionFamilyName)
+    //    new TransactionTemplate[T](defaultTxBuilder.build) {
     new TransactionTemplate[T]() { // FIXME take factory
-      def execute(mtx: MultiverseTransaction): T = body
+      def execute(mtx: MultiverseTransaction): T = {
+        val result = body
+
+        log.trace("Committing transaction [%s] \nwith family name [%s] \nby joining transaction set")
+        getTransactionSetInScope.joinCommit(mtx)
+
+        // FIXME tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS) 
+        //getTransactionSetInScope.tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS)
+
+        clearTransaction
+        result
+      }
 
       override def onStart(mtx: MultiverseTransaction) = {
+        val txSet = if (!isTransactionSetInScope) createNewTransactionSet
+                    else getTransactionSetInScope
         val tx = new Transaction
         tx.transaction = Some(mtx)
         setTransaction(Some(tx))
-      }
 
-      override def onPostCommit = {
-        if (isTransactionInScope) getTransactionInScope.commit
-        else throw new IllegalStateException("No transaction in scope")
+        txSet.registerOnCommitTask(new Runnable() {
+          def run = tx.commit
+        })
+        txSet.registerOnAbortTask(new Runnable() {
+          def run = tx.abort
+        })
       }
     }.execute()
   }
 
-  /**
-   * See ScalaDoc on class.
-   */
-  def atomic[T](retryCount: Int)(body: => T)(implicit transactionFamilyName: String): T = {
-    new TransactionTemplate[T]() { // FIXME take factory
-      def execute(mtx: MultiverseTransaction): T = body
-
-      override def onStart(mtx: MultiverseTransaction) = {
-        val tx = new Transaction
-        tx.transaction = Some(mtx)
-        setTransaction(Some(tx))
-      }
-
-      override def onPostCommit = {
-        if (isTransactionInScope) getTransactionInScope.commit
-        else throw new IllegalStateException("No transaction in scope")
-      }
-    }.execute
-  }
-
-  /**
-   * See ScalaDoc on class.
-   */
-  def atomicReadOnly[T](retryCount: Int)(body: => T)(implicit transactionFamilyName: String): T = {
-    new TransactionTemplate[T]() { // FIXME take factory
-      def execute(mtx: MultiverseTransaction): T = body
-
-      override def onStart(mtx: MultiverseTransaction) = {
-        val tx = new Transaction
-        tx.transaction = Some(mtx)
-        setTransaction(Some(tx))
-      }
-
-      override def onPostCommit = {
-        if (isTransactionInScope) getTransactionInScope.commit
-        else throw new IllegalStateException("No transaction in scope")
-      }
-    }.execute
-  }
-
-  /**
-   * See ScalaDoc on class.
-   */
-  def atomicReadOnly[T](body: => T): T = {
-    new TransactionTemplate[T]() { // FIXME take factory
-      def execute(mtx: MultiverseTransaction): T = body
-
-      override def onStart(mtx: MultiverseTransaction) = {
-        val tx = new Transaction
-        tx.transaction = Some(mtx)
-        setTransaction(Some(tx))
-      }
-
-      override def onPostCommit = {
-        if (isTransactionInScope) getTransactionInScope.commit
-        else throw new IllegalStateException("No transaction in scope")
-      }
-    }.execute
-  }
-
   /**
    * See ScalaDoc on class.
    */
@@ -215,7 +195,6 @@ object Transaction extends TransactionManagement {
   def elseBody[A](firstBody: => A) = new {
     def orElse(secondBody: => A) = new OrElseTemplate[A] {
       def run(t: MultiverseTransaction) = firstBody
-
       def orelserun(t: MultiverseTransaction) = secondBody
     }.execute()
   }
@@ -227,37 +206,34 @@ object Transaction extends TransactionManagement {
 @serializable class Transaction extends Logging {
   import Transaction._
 
+  log.trace("Creating %s", toString)
   val id = Transaction.idFactory.incrementAndGet
   @volatile private[this] var status: TransactionStatus = TransactionStatus.New
   private[akka] var transaction: Option[MultiverseTransaction] = None
   private[this] val persistentStateMap = new HashMap[String, Committable]
   private[akka] val depth = new AtomicInteger(0)
-  //private[akka] val transactionSet = new VetoCommitBarrier
-
-  //transactionSet.vetoCommit(this)
 
   // --- public methods ---------
 
-  def abort = synchronized {
-  //  transactionSet.abort
-  }
-
   def commit = synchronized {
+    log.trace("Committing transaction %s", toString)    
     pureAtomic {
       persistentStateMap.values.foreach(_.commit)
-      TransactionManagement.clearTransaction
     }
-    //transactionSet.vetoCommit(this)
     status = TransactionStatus.Completed
   }
 
-  def isNew = synchronized {status == TransactionStatus.New}
+  def abort = synchronized {
+    log.trace("Aborting transaction %s", toString)    
+  }
 
-  def isActive = synchronized {status == TransactionStatus.Active}
+  def isNew = synchronized { status == TransactionStatus.New }
 
-  def isCompleted = synchronized {status == TransactionStatus.Completed}
+  def isActive = synchronized { status == TransactionStatus.Active }
 
-  def isAborted = synchronized {status == TransactionStatus.Aborted}
+  def isCompleted = synchronized { status == TransactionStatus.Completed }
+
+  def isAborted = synchronized { status == TransactionStatus.Aborted }
 
   // --- internal methods ---------
 
@@ -300,9 +276,9 @@ object Transaction extends TransactionManagement {
         that.asInstanceOf[Transaction].id == this.id
   }
 
-  override def hashCode(): Int = synchronized {id.toInt}
+  override def hashCode(): Int = synchronized { id.toInt }
 
-  override def toString(): String = synchronized {"Transaction[" + id + ", " + status + "]"}
+  override def toString = synchronized { "Transaction[" + id + ", " + status + "]" }
 }
 
 /**
diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala
index 1f2ede3024..60a6ae6de3 100644
--- a/akka-core/src/main/scala/stm/TransactionManagement.scala
+++ b/akka-core/src/main/scala/stm/TransactionManagement.scala
@@ -9,53 +9,80 @@ import java.util.concurrent.atomic.AtomicBoolean
 import se.scalablesolutions.akka.util.Logging
 
 import org.multiverse.api.ThreadLocalTransaction._
+import org.multiverse.commitbarriers.CountDownCommitBarrier
 
 class StmException(msg: String) extends RuntimeException(msg)
 
-class TransactionAwareWrapperException(
-    val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) {
-  override def toString(): String = "TransactionAwareWrapperException[" + cause + ", " + tx + "]"
+class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) {
+  override def toString = "TransactionAwareWrapperException[" + cause + ", " + tx + "]"
 }
 
 object TransactionManagement extends TransactionManagement {
   import se.scalablesolutions.akka.Config._
 
-  val MAX_NR_OF_RETRIES = config.getInt("akka.stm.max-nr-of-retries", 100)
-  val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", false))
-
+  val TRANSACTION_ENABLED =      new AtomicBoolean(config.getBool("akka.stm.service", false))
+  val FAIR_TRANSACTIONS =        config.getBool("akka.stm.fair", true)
+  val INTERRUPTIBLE =            config.getBool("akka.stm.interruptible", true)
+  val MAX_NR_OF_RETRIES =        config.getInt("akka.stm.max-nr-of-retries", 1000)
+  val TRANSACTION_TIMEOUT =      config.getInt("akka.stm.timeout", 10000)
+  val SMART_TX_LENGTH_SELECTOR = config.getBool("akka.stm.smart-tx-length-selector", true)
   def isTransactionalityEnabled = TRANSACTION_ENABLED.get
+
   def disableTransactions = TRANSACTION_ENABLED.set(false)
 
-  private[akka] val currentTransaction = new ThreadLocal[Option[Transaction]]() {
+  private[akka] val transactionSet = new ThreadLocal[Option[CountDownCommitBarrier]]() {
+    override protected def initialValue: Option[CountDownCommitBarrier] = None
+  }
+
+  private[akka] val transaction = new ThreadLocal[Option[Transaction]]() {
     override protected def initialValue: Option[Transaction] = None
   }
+
+  private[akka] def getTransactionSet: CountDownCommitBarrier = {
+    val option = transactionSet.get
+    if ((option eq null) || option.isEmpty) throw new IllegalStateException("No TransactionSet in scope")
+    option.get
+  }
+
+  private[akka] def getTransaction: Transaction = {
+    val option = transaction.get
+    if ((option eq null) || option.isEmpty) throw new IllegalStateException("No Transaction in scope")
+    option.get
+  }
 }
 
 trait TransactionManagement extends Logging {
-  import TransactionManagement.currentTransaction
 
-  private[akka] def createNewTransaction = currentTransaction.set(Some(new Transaction))
-
-  private[akka] def setTransaction(transaction: Option[Transaction]) = if (transaction.isDefined) {
-    val tx = transaction.get
-    currentTransaction.set(transaction)
-    if (tx.transaction.isDefined) setThreadLocalTransaction(tx.transaction.get)
-    else throw new IllegalStateException("No transaction defined")
+  private[akka] def createNewTransactionSet: CountDownCommitBarrier = {
+    val txSet = new CountDownCommitBarrier(1, TransactionManagement.FAIR_TRANSACTIONS)
+    TransactionManagement.transactionSet.set(Some(txSet))
+    txSet
   }
 
+  private[akka] def setTransactionSet(txSet: Option[CountDownCommitBarrier]) =
+    if (txSet.isDefined) TransactionManagement.transactionSet.set(txSet)
+
+  private[akka] def setTransaction(tx: Option[Transaction]) =
+    if (tx.isDefined) TransactionManagement.transaction.set(tx)
+
+  private[akka] def clearTransactionSet = TransactionManagement.transactionSet.set(None)
+
   private[akka] def clearTransaction = {
-    currentTransaction.set(None)
+    TransactionManagement.transaction.set(None)
     setThreadLocalTransaction(null)
   }
 
-  private[akka] def getTransactionInScope = currentTransaction.get.get
-  
-  private[akka] def isTransactionInScope = currentTransaction.get.isDefined
+  private[akka] def getTransactionSetInScope = TransactionManagement.getTransactionSet
 
-  private[akka] def isTransactionTopLevel = if (isTransactionInScope) getTransactionInScope.isTopLevel
+  private[akka] def getTransactionInScope = TransactionManagement.getTransaction
 
-  private[akka] def incrementTransaction = if (isTransactionInScope) getTransactionInScope.increment
-
-  private[akka] def decrementTransaction = if (isTransactionInScope) getTransactionInScope.decrement
-}
+  private[akka] def isTransactionSetInScope = {
+    val option = TransactionManagement.transactionSet.get
+    (option ne null) && option.isDefined
+  }
 
+  private[akka] def isTransactionInScope = {
+    val option = TransactionManagement.transaction.get
+    (option ne null) && option.isDefined
+  }
+}
\ No newline at end of file
diff --git a/akka-core/src/main/scala/stm/TransactionalState.scala b/akka-core/src/main/scala/stm/TransactionalState.scala
index 195b134271..1b52faf969 100644
--- a/akka-core/src/main/scala/stm/TransactionalState.scala
+++ b/akka-core/src/main/scala/stm/TransactionalState.scala
@@ -77,7 +77,7 @@ class TransactionalRef[T] extends Transactional {
   implicit val txInitName = "TransactionalRef:Init"
   val uuid = UUID.newUuid.toString
 
-  private[this] val ref: AlphaRef[T] = atomic { new AlphaRef }
+  private[this] lazy val ref: AlphaRef[T] = new AlphaRef
 
   def swap(elem: T) = {
     ensureIsInTransaction
diff --git a/akka-core/src/test/scala/InMemoryActorTest.scala b/akka-core/src/test/scala/InMemoryActorTest.scala
index cd06b80d0a..d4be98fcaa 100644
--- a/akka-core/src/test/scala/InMemoryActorTest.scala
+++ b/akka-core/src/test/scala/InMemoryActorTest.scala
@@ -23,7 +23,7 @@ case class SuccessOneWay(key: String, value: String)
 case class FailureOneWay(key: String, value: String, failer: Actor)
 
 class InMemStatefulActor extends Actor {
-  timeout = 100000
+  timeout = 5000
   makeTransactionRequired
 
   private lazy val mapState = TransactionalState.newMap[String, String]
@@ -86,8 +86,8 @@ class InMemFailerActor extends Actor {
 }
                                                         
 class InMemoryActorTest extends JUnitSuite {
+  import Actor.Sender.Self
 
-  /*
   @Test
   def shouldOneWayMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -98,7 +98,7 @@ class InMemoryActorTest extends JUnitSuite {
     Thread.sleep(1000)
     assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get)
   }
-  */
+
   @Test
   def shouldMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -107,7 +107,7 @@ class InMemoryActorTest extends JUnitSuite {
     stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
     assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get)
   }
-  /*
+
   @Test
   def shouldOneWayMapShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
@@ -120,7 +120,7 @@ class InMemoryActorTest extends JUnitSuite {
     Thread.sleep(1000)
     assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state
   }
-  */
+
   @Test
   def shouldMapShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
@@ -134,7 +134,7 @@ class InMemoryActorTest extends JUnitSuite {
     } catch {case e: RuntimeException => {}}
     assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state
   }
-  /*
+
   @Test
   def shouldOneWayVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -145,7 +145,7 @@ class InMemoryActorTest extends JUnitSuite {
     Thread.sleep(1000)
     assert(2 === (stateful !! GetVectorSize).get)
   }
-  */
+
   @Test
   def shouldVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -154,7 +154,7 @@ class InMemoryActorTest extends JUnitSuite {
     stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
     assert(2 === (stateful !! GetVectorSize).get)
   }
-  /*
+
   @Test
   def shouldOneWayVectorShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
@@ -167,7 +167,7 @@ class InMemoryActorTest extends JUnitSuite {
     Thread.sleep(1000)
     assert(1 === (stateful !! GetVectorSize).get)
   }
-  */
+
   @Test
   def shouldVectorShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
@@ -181,7 +181,7 @@ class InMemoryActorTest extends JUnitSuite {
     } catch {case e: RuntimeException => {}}
     assert(1 === (stateful !! GetVectorSize).get)
   }
-  /*
+
   @Test
   def shouldOneWayRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -192,7 +192,7 @@ class InMemoryActorTest extends JUnitSuite {
     Thread.sleep(1000)
     assert("new state" === (stateful !! GetRefState).get)
   }
-  */
+
   @Test
   def shouldRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
     val stateful = new InMemStatefulActor
@@ -201,7 +201,7 @@ class InMemoryActorTest extends JUnitSuite {
     stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
     assert("new state" === (stateful !! GetRefState).get)
   }
-  /*
+
   @Test
   def shouldOneWayRefShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
@@ -212,9 +212,9 @@ class InMemoryActorTest extends JUnitSuite {
     failer.start
     stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
     Thread.sleep(1000)
-    assert("init" === (stateful !! GetRefState).get) // check that state is == init state
+    assert("init" === (stateful !! (GetRefState, 1000000)).get) // check that state is == init state
   }
-  */
+
   @Test
   def shouldRefShouldRollbackStateForStatefulServerInCaseOfFailure = {
     val stateful = new InMemStatefulActor
diff --git a/akka-core/src/test/scala/ShutdownSpec.scala b/akka-core/src/test/scala/ShutdownSpec.scala
index ba03fbe902..20927bbfb1 100644
--- a/akka-core/src/test/scala/ShutdownSpec.scala
+++ b/akka-core/src/test/scala/ShutdownSpec.scala
@@ -2,9 +2,8 @@ package se.scalablesolutions.akka.remote
 
 import se.scalablesolutions.akka.actor.Actor
 
-object ActorShutdownSpec {
+object ActorShutdownRunner {
   def main(args: Array[String]) {
-
     class MyActor extends Actor {
       def receive = {
         case "test" => println("received test")
@@ -22,7 +21,7 @@ object ActorShutdownSpec {
 
 // case 2
 
-object RemoteServerAndClusterShutdownSpec {
+object RemoteServerAndClusterShutdownRunner {
   def main(args: Array[String]) {
     val s1 = new RemoteServer
     val s2 = new RemoteServer
diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java
index 8a51feed6b..366403ef46 100644
--- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java
+++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemNestedStateTest.java
@@ -11,7 +11,7 @@ import static se.scalablesolutions.akka.config.JavaConfig.*;
 import se.scalablesolutions.akka.actor.*;
 import se.scalablesolutions.akka.Kernel;
 import junit.framework.TestCase;
-/*
+
 public class InMemNestedStateTest extends TestCase {
   static String messageLog = "";
 
@@ -133,4 +133,3 @@ public class InMemNestedStateTest extends TestCase {
     assertEquals("init", nested.getRefState()); // check that state is == init state
   }
 }
-*/
\ No newline at end of file
diff --git a/akka-patterns/src/main/scala/Agent.scala b/akka-patterns/src/main/scala/Agent.scala
index 4dd8640c32..aea74530a3 100644
--- a/akka-patterns/src/main/scala/Agent.scala
+++ b/akka-patterns/src/main/scala/Agent.scala
@@ -50,30 +50,30 @@ sealed class Agent[T] private (initialValue: T) extends Actor {
   * Periodically handles incoming messages
   */
   def receive = {
-    case FunctionHolder(fun: (T => T)) => atomic { updateData(fun(value.getOrWait)) }
+    case FunctionHolder(fun: (T => T)) => updateData(fun(value.getOrWait))
 
     case ValueHolder(x: T) => updateData(x)
 
-    case ProcedureHolder(fun: (T => Unit)) => atomic { fun(copyStrategy(value.getOrWait)) }
+    case ProcedureHolder(fun: (T => Unit)) => fun(copyStrategy(value.getOrWait))
   }
 
   /**
    * Specifies how a copy of the value is made, defaults to using identity
    */
-  protected def copyStrategy(t : T) : T = t
+  protected def copyStrategy(t: T): T = t
 
 
   /**
   * Updates the internal state with the value provided as a by-name parameter
   */
-  private final def updateData(newData: => T) : Unit = atomic { value.swap(newData) }
+  private final def updateData(newData: => T): Unit = value.swap(newData)
 
   /**
   * Submits a request to read the internal state.
   * A copy of the internal state will be returned, depending on the underlying effective copyStrategy.
   * Internally leverages the asynchronous getValue() method and then waits for its result on a CountDownLatch.
   */
-  final def get : T = {
+  final def get: T = {
     val ref = new AtomicReference[T]
     val latch = new CountDownLatch(1)
     get((x: T) => {ref.set(x); latch.countDown})
@@ -85,14 +85,14 @@ sealed class Agent[T] private (initialValue: T) extends Actor {
   * Asynchronously submits a request to read the internal state. The supplied function will be executed on the returned internal state value.
   * A copy of the internal state will be used, depending on the underlying effective copyStrategy.
   */
-  final def get(message: (T => Unit)) : Unit = this ! ProcedureHolder(message)
+  final def get(message: (T => Unit)): Unit = this ! ProcedureHolder(message)
 
   /**
   * Submits a request to read the internal state.
   * A copy of the internal state will be returned, depending on the underlying effective copyStrategy.
   * Internally leverages the asynchronous getValue() method and then waits for its result on a CountDownLatch.
   */
-  final def apply() : T = get
+  final def apply(): T = get
 
   /**
   * Asynchronously submits a request to read the internal state. The supplied function will be executed on the returned internal state value.
@@ -103,22 +103,22 @@ sealed class Agent[T] private (initialValue: T) extends Actor {
   /**
   * Submits the provided function for execution against the internal agent's state
   */
-  final def apply(message: (T => T)) : Unit = this ! FunctionHolder(message)
+  final def apply(message: (T => T)): Unit = this ! FunctionHolder(message)
 
   /**
   * Submits a new value to be set as the new agent's internal state
   */
-  final def apply(message: T) : Unit = this ! ValueHolder(message)
+  final def apply(message: T): Unit = this ! ValueHolder(message)
 
   /**
   * Submits the provided function for execution against the internal agent's state
   */
-  final def update(message: (T => T)) : Unit = this ! FunctionHolder(message)
+  final def update(message: (T => T)): Unit = this ! FunctionHolder(message)
 
   /**
   * Submits a new value to be set as the new agent's internal state
   */
-  final def update(message: T) : Unit = this ! ValueHolder(message)
+  final def update(message: T): Unit = this ! ValueHolder(message)
 }
 
 /**
@@ -135,7 +135,7 @@ object Agent {
   /**
    * Creates a new Agent of type T with the initial value of value
    */
-  def apply[T](value:T) : Agent[T] = new Agent(value)
+  def apply[T](value:T): Agent[T] = new Agent(value)
 
   /**
    * Creates a new Agent of type T with the initial value of value and with the specified copy function
diff --git a/akka-patterns/src/test/scala/ActorPatternsTest.scala b/akka-patterns/src/test/scala/ActorPatternsTest.scala
index 11f2664640..ae6ae5c0e8 100644
--- a/akka-patterns/src/test/scala/ActorPatternsTest.scala
+++ b/akka-patterns/src/test/scala/ActorPatternsTest.scala
@@ -21,12 +21,12 @@ class ActorPatternsTest extends junit.framework.TestCase with Suite with MustMat
       val (testMsg1,testMsg2,testMsg3,testMsg4) = ("test1","test2","test3","test4")
 
       var targetOk = 0
-      val t1 = actor() receive {
+      val t1: Actor = actor {
         case `testMsg1` => targetOk += 2
         case `testMsg2` => targetOk += 4
       }
 
-      val t2 = actor() receive {
+      val t2: Actor = actor {
           case `testMsg3` => targetOk += 8
       }
 
@@ -48,7 +48,7 @@ class ActorPatternsTest extends junit.framework.TestCase with Suite with MustMat
   @Test def testLogger = verify(new TestActor {
     def test = {
       val msgs = new HashSet[Any]
-      val t1 = actor() receive {
+      val t1: Actor = actor {
         case _ =>
       }
       val l = loggerActor(t1,(x) => msgs += x)
diff --git a/akka-patterns/src/test/scala/AgentTest.scala b/akka-patterns/src/test/scala/AgentTest.scala
index 17ccce8e0a..25961f8222 100644
--- a/akka-patterns/src/test/scala/AgentTest.scala
+++ b/akka-patterns/src/test/scala/AgentTest.scala
@@ -7,18 +7,23 @@ import org.scalatest.junit.JUnitRunner
 import org.scalatest.matchers.MustMatchers
 import org.junit.{Test}
 
+/*
 @RunWith(classOf[JUnitRunner])
 class AgentTest extends junit.framework.TestCase with Suite with MustMatchers with ActorTestUtil with Logging {
-  @Test def testAgent = verify(new TestActor {
-     def test = {
-        val t = Agent(5)
-        handle(t){
-        t.update( _ + 1 )
-        t.update( _ * 2 )
 
-        val r = t()
-        r must be (12)
-       }
-     }
+  @Test def testAgent = verify(new TestActor {
+    def test = {
+      atomic {
+        val t = Agent(5)
+        handle(t) {
+          t.update(_ + 1)
+          t.update(_ * 2)
+
+          val r = t()
+          r must be(12)
+        }
+      }
+    }
   })
-}
\ No newline at end of file
+}
+*/
\ No newline at end of file
diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala
index dc55e0eca1..b4ea8fc381 100644
--- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala
+++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala
@@ -4,7 +4,7 @@
 
 package se.scalablesolutions.akka.state
 
-import se.scalablesolutions.akka.stm.TransactionManagement.currentTransaction
+import se.scalablesolutions.akka.stm.TransactionManagement.transaction
 import se.scalablesolutions.akka.collection._
 import se.scalablesolutions.akka.util.Logging
 
@@ -64,10 +64,6 @@ trait Storage {
     throw new UnsupportedOperationException
 }
 
-
-
-
-
 /**
  * Implementation of PersistentMap for every concrete 
  * storage will have the same workflow. This abstracts the workflow.
@@ -162,8 +158,8 @@ trait PersistentMap[K, V] extends scala.collection.mutable.Map[K, V]
   }
 
   private def register = {
-    if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
-    currentTransaction.get.get.register(uuid, this)
+    if (transaction.get.isEmpty) throw new NoTransactionInScopeException
+    transaction.get.get.register(uuid, this)
   }
 }
 
@@ -236,8 +232,8 @@ trait PersistentVector[T] extends RandomAccessSeq[T] with Transactional with Com
   def length: Int = storage.getVectorStorageSizeFor(uuid) + newElems.length
 
   private def register = {
-    if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
-    currentTransaction.get.get.register(uuid, this)
+    if (transaction.get.isEmpty) throw new NoTransactionInScopeException
+    transaction.get.get.register(uuid, this)
   }
 }
 
@@ -272,8 +268,8 @@ trait PersistentRef[T] extends Transactional with Committable {
   }
 
   private def register = {
-    if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
-    currentTransaction.get.get.register(uuid, this)
+    if (transaction.get.isEmpty) throw new NoTransactionInScopeException
+    transaction.get.get.register(uuid, this)
   }
 }
 
@@ -397,7 +393,7 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A]
     throw new UnsupportedOperationException("dequeueAll not supported")
 
   private def register = {
-    if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
-    currentTransaction.get.get.register(uuid, this)
+    if (transaction.get.isEmpty) throw new NoTransactionInScopeException
+    transaction.get.get.register(uuid, this)
   }
 }
diff --git a/akka.iml b/akka.iml
index 2f07a75716..a39c87020f 100644
--- a/akka.iml
+++ b/akka.iml
@@ -2,7 +2,18 @@
 
   
     
-      
+      
+        
+        
+      
     
   
   
diff --git a/config/akka-reference.conf b/config/akka-reference.conf
index 749b599e0b..296b06428b 100644
--- a/config/akka-reference.conf
+++ b/config/akka-reference.conf
@@ -30,8 +30,10 @@
  
   
     service = on
-    max-nr-of-retries = 100
-    distributed = off           # not implemented yet
+    fair = on                     # should transactions be fair or non-fair (non fair yield better performance)
+    max-nr-of-retries = 1000      # max nr of retries of a failing transaction before giving up
+    timeout = 10000               # transaction timeout; if transaction have not committed within the timeout then it is aborted
+    distributed = off             # not implemented yet
   
  
   
diff --git a/pom.xml b/pom.xml
index d2885abf61..480ad8723a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -161,6 +161,11 @@
       Codehaus Maven Repository
       http://repository.codehaus.org
     
+    
+      snapshots.repository.codehaus.org
+      Codehaus Maven Snapshot Repository
+      http://snapshots.repository.codehaus.org
+    
     
       maven2-repository.dev.java.net
       Java.net Repository for Maven
@@ -301,11 +306,11 @@
         
           
             **/*Test.java
-            
+            **/*Spec.java
           
-          
+          
           
             
               akka.home

From 17ffeb3cf9e391989d578031c83238a47ca2f7f5 Mon Sep 17 00:00:00 2001
From: Martin Krasser 
Date: Sun, 28 Feb 2010 09:13:23 +0100
Subject: [PATCH 06/81] Fixed actor deregistration-by-id issue and added
 ActorRegistry unit test.

---
 .../src/main/scala/actor/ActorRegistry.scala  | 17 +++++-
 .../src/test/scala/ActorRegistryTest.scala    | 55 +++++++++++++++++++
 2 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 akka-core/src/test/scala/ActorRegistryTest.scala

diff --git a/akka-core/src/main/scala/actor/ActorRegistry.scala b/akka-core/src/main/scala/actor/ActorRegistry.scala
index 509750340e..7ca40d1a82 100644
--- a/akka-core/src/main/scala/actor/ActorRegistry.scala
+++ b/akka-core/src/main/scala/actor/ActorRegistry.scala
@@ -10,13 +10,15 @@ import scala.collection.mutable.{ListBuffer, HashMap}
 import scala.reflect.Manifest
 
 /**
- * Registry holding all actor instances, mapped by class and the actor's id field (which can be set by user-code).
+ * Registry holding all actor instances, mapped by class, the actor's uuid and the actor's id field (which can be set
+ * by user-code).
  *
  * @author Jonas Bonér
  */
 object ActorRegistry extends Logging {
   private val actorsByClassName = new HashMap[String, List[Actor]]
   private val actorsById = new HashMap[String, List[Actor]]
+  private val actorsByUuid = new HashMap[String, Actor]
 
   /**
    * Returns all actors in the system.
@@ -50,7 +52,7 @@ object ActorRegistry extends Logging {
   }
 
   /**
-   * Finds all actors that has a specific id.
+   * Finds all actors that have a specific id.
    */
   def actorsFor(id : String): List[Actor] = synchronized {
     actorsById.get(id) match {
@@ -59,6 +61,13 @@ object ActorRegistry extends Logging {
     }
   }
 
+  /**
+   * Finds the actor that has a specific uuid.
+   */
+  def actorFor(uuid : String): Option[Actor] = synchronized {
+    actorsByUuid.get(uuid)
+  }
+
   def register(actor: Actor) = synchronized {
     val className = actor.getClass.getName
     actorsByClassName.get(className) match {
@@ -71,11 +80,13 @@ object ActorRegistry extends Logging {
       case Some(instances) => actorsById + (id -> (actor :: instances))
       case None => actorsById + (id -> (actor :: Nil))
     }
+    actorsByUuid + (actor.uuid -> actor)
   }
 
   def unregister(actor: Actor) = synchronized {
     actorsByClassName - actor.getClass.getName
-    actorsById - actor.getClass.getName
+    actorsById - actor.getId
+    actorsByUuid - actor.uuid
   }
 
   def shutdownAll = {
diff --git a/akka-core/src/test/scala/ActorRegistryTest.scala b/akka-core/src/test/scala/ActorRegistryTest.scala
new file mode 100644
index 0000000000..4c0292abcf
--- /dev/null
+++ b/akka-core/src/test/scala/ActorRegistryTest.scala
@@ -0,0 +1,55 @@
+package se.scalablesolutions.akka.actor
+
+import org.junit.Assert._
+import org.scalatest.junit.JUnitSuite
+import org.junit.Test
+
+class ActorRegistryTest extends JUnitSuite {
+
+  val registry = ActorRegistry
+
+  @Test
+  def testRegistrationWithDefaultId {
+    val actor = new TestActor1
+    assertEquals(actor.getClass.getName, actor.getId)
+    testRegistration(actor, classOf[TestActor1])
+  }
+
+  @Test
+  def testRegistrationWithCustomId {
+    val actor = new TestActor2
+    assertEquals("customid", actor.getId)
+    testRegistration(actor, classOf[TestActor2])
+  }
+
+  private def testRegistration[T <: Actor](actor: T, actorClass: Class[T]) {
+    assertEquals("non-started actor registered", Nil, registry.actorsFor(actorClass))
+    assertEquals("non-started actor registered", Nil, registry.actorsFor(actor.getId))
+    assertEquals("non-started actor registered", None, registry.actorFor(actor.uuid))
+    actor.start
+    assertEquals("actor not registered", List(actor), registry.actorsFor(actorClass))
+    assertEquals("actor not registered", List(actor), registry.actorsFor(actor.getId))
+    assertEquals("actor not registered", Some(actor), registry.actorFor(actor.uuid))
+    actor.stop
+    assertEquals("stopped actor registered", Nil, registry.actorsFor(actorClass))
+    assertEquals("stopped actor registered", Nil, registry.actorsFor(actor.getId))
+    assertEquals("stopped actor registered", None, registry.actorFor(actor.uuid))
+  }
+
+}
+
+class TestActor1 extends Actor {
+
+  // use default id
+
+  protected def receive = null
+
+}
+
+class TestActor2 extends Actor {
+
+  id = "customid"
+
+  protected def receive = null
+
+}
\ No newline at end of file

From 94483e102d6f5c74a61f8b3130d08346540c0b3c Mon Sep 17 00:00:00 2001
From: Martin Krasser 
Date: Mon, 1 Mar 2010 06:07:32 +0100
Subject: [PATCH 07/81] changed actor URI format, cleanup unit tests.

---
 .../{CamelConsumer.scala => Consumer.scala}   |  2 +-
 .../main/scala/component/ActorComponent.scala | 58 +++++------
 .../src/main/scala/service/CamelService.scala | 20 ++--
 .../scala/component/ActorComponentTest.scala  | 70 ++++++--------
 .../test/scala/service/CamelServiceTest.scala | 95 +++++++++----------
 akka-core/src/test/scala/SerializerTest.scala |  6 +-
 .../src/main/scala/Consumer1.scala            |  4 +-
 7 files changed, 121 insertions(+), 134 deletions(-)
 rename akka-camel/src/main/scala/{CamelConsumer.scala => Consumer.scala} (94%)

diff --git a/akka-camel/src/main/scala/CamelConsumer.scala b/akka-camel/src/main/scala/Consumer.scala
similarity index 94%
rename from akka-camel/src/main/scala/CamelConsumer.scala
rename to akka-camel/src/main/scala/Consumer.scala
index f3518e03f6..3dbb101292 100644
--- a/akka-camel/src/main/scala/CamelConsumer.scala
+++ b/akka-camel/src/main/scala/Consumer.scala
@@ -11,7 +11,7 @@ import se.scalablesolutions.akka.actor.Actor
  *
  * @author Martin Krasser
  */
-trait CamelConsumer {
+trait Consumer {
 
   self: Actor =>
 
diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala
index 4c99bfd809..b85cf4829f 100644
--- a/akka-camel/src/main/scala/component/ActorComponent.scala
+++ b/akka-camel/src/main/scala/component/ActorComponent.scala
@@ -27,12 +27,13 @@ class ActorComponent extends DefaultComponent {
     new ActorEndpoint(uri, this, idAndUuid._1, idAndUuid._2)
   }
   
-  private def idAndUuidPair(remaining: String): Tuple2[String, Option[String]] = {
-    remaining split "/" toList match {
-      case id :: Nil         => (id, None)
-      case id :: uuid :: Nil => (id, Some(uuid))
+  private def idAndUuidPair(remaining: String): Tuple2[Option[String], Option[String]] = {
+    remaining split ":" toList match {
+      case             id :: Nil => (Some(id), None)
+      case   "id" ::   id :: Nil => (Some(id), None)
+      case "uuid" :: uuid :: Nil => (None, Some(uuid))
       case _ => throw new IllegalArgumentException(
-        "invalid path format: %s - should be [/]" format remaining)
+        "invalid path format: %s - should be  or id: or uuid:" format remaining)
     }
   }
 
@@ -40,19 +41,17 @@ class ActorComponent extends DefaultComponent {
 
 /**
  * Camel endpoint for interacting with actors. An actor can be addressed by its
- * Actor.id or by an Actor.id - Actor.uuid
- * combination. The URI format is actor://[/].
+ * Actor.getId or its Actor.uuid combination. Supported URI formats are
+ * actor:<actorid>,
+ * actor:id:<actorid> and
+ * actor:uuid:<actoruuid>.
  *
  * @see se.scalablesolutions.akka.camel.component.ActorComponent
  * @see se.scalablesolutions.akka.camel.component.ActorProducer
 
  * @author Martin Krasser
  */
-class ActorEndpoint(uri: String, comp: ActorComponent, val id: String, val uuid: Option[String]) extends DefaultEndpoint(uri, comp) {
-
-  // TODO: clarify uuid details
-  // - do they change after persist/restore
-  // - what about remote actors and uuids
+class ActorEndpoint(uri: String, comp: ActorComponent, val id: Option[String], val uuid: Option[String]) extends DefaultEndpoint(uri, comp) {
 
   /**
    * @throws UnsupportedOperationException 
@@ -80,10 +79,10 @@ class ActorEndpoint(uri: String, comp: ActorComponent, val id: String, val uuid:
  */
 class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
 
-  implicit val sender = Some(Sender)
+  implicit val sender = Some(new Sender)
 
   def process(exchange: Exchange) {
-    val actor = target getOrElse (throw new ActorNotRegisteredException(ep.id, ep.uuid))
+    val actor = target getOrElse (throw new ActorNotRegisteredException(ep.getEndpointUri))
     if (exchange.getPattern.isOutCapable)
       processInOut(exchange, actor)
     else
@@ -92,6 +91,12 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
 
   override def start {
     super.start
+    sender.get.start
+  }
+
+  override def stop {
+    sender.get.stop
+    super.stop
   }
 
   protected def receive = {
@@ -122,16 +127,17 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
   }
 
   private def target: Option[Actor] = {
-    ActorRegistry.actorsFor(ep.id) match {
-      case actor :: Nil if targetMatchesUuid(actor) => Some(actor)
-      case Nil    => None
-      case actors => actors find (targetMatchesUuid _)
-    }
+    if (ep.id.isDefined) targetById(ep.id.get)
+    else targetByUuid(ep.uuid.get)
   }
 
-  private def targetMatchesUuid(target: Actor): Boolean =
-    // if ep.uuid is not defined always return true
-    target.uuid == (ep.uuid getOrElse target.uuid)
+  private def targetById(id: String) = ActorRegistry.actorsFor(id) match {
+    case Nil          => None
+    case actor :: Nil => Some(actor)
+    case actors       => Some(actors.first)
+  }
+
+  private def targetByUuid(uuid: String) = ActorRegistry.actorFor(uuid)
 
 }
 
@@ -140,9 +146,7 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
  *
  * @author Martin Krasser
  */
-private[component] object Sender extends Actor {
-
-  start
+private[component] class Sender extends Actor {
 
   /**
    * Ignores any message.
@@ -159,8 +163,8 @@ private[component] object Sender extends Actor {
  *
  * @author Martin Krasser
  */
-class ActorNotRegisteredException(name: String, uuid: Option[String]) extends RuntimeException {
+class ActorNotRegisteredException(uri: String) extends RuntimeException {
 
-  override def getMessage = "actor(id=%s,uuid=%s) not registered" format (name, uuid getOrElse "")
+  override def getMessage = "%s not registered" format uri
 
 }
\ No newline at end of file
diff --git a/akka-camel/src/main/scala/service/CamelService.scala b/akka-camel/src/main/scala/service/CamelService.scala
index 2811ab88fe..98a767797b 100644
--- a/akka-camel/src/main/scala/service/CamelService.scala
+++ b/akka-camel/src/main/scala/service/CamelService.scala
@@ -10,7 +10,7 @@ import org.apache.camel.builder.RouteBuilder
 
 import se.scalablesolutions.akka.actor.{Actor, ActorRegistry}
 import se.scalablesolutions.akka.annotation.consume
-import se.scalablesolutions.akka.camel.CamelConsumer
+import se.scalablesolutions.akka.camel.Consumer
 import se.scalablesolutions.akka.util.{Bootable, Logging}
 
 /**
@@ -43,7 +43,7 @@ trait CamelService extends Bootable with Logging {
 /**
  * Generic route builder that searches the registry for actors that are
  * either annotated with @se.scalablesolutions.akka.annotation.consume or
- * mixed in se.scalablesolutions.akka.camel.CamelConsumer and exposes them
+ * mixed in se.scalablesolutions.akka.camel.Consumer and exposes them
  * as Camel endpoints.
  *
  * @author Martin Krasser
@@ -55,25 +55,23 @@ class CamelRouteBuilder extends RouteBuilder with Logging {
 
     //
     // TODO: resolve/clarify issues with ActorRegistry
-    //       - custom Actor.id ignored
-    //       - actor de-registration issues
     //       - multiple registration with same id/uuid possible
     //
 
     // TODO: avoid redundant registrations
     actors.filter(isConsumeAnnotated _).foreach { actor: Actor =>
       val fromUri = actor.getClass.getAnnotation(classOf[consume]).value()
-      configure(fromUri, "actor://%s" format actor.id)
+      configure(fromUri, "actor:id:%s" format actor.getId)
       log.debug("registered actor (id=%s) for consuming messages from %s "
-          format (actor.id, fromUri))
+          format (actor.getId, fromUri))
     }
 
     // TODO: avoid redundant registrations
     actors.filter(isConsumerInstance _).foreach { actor: Actor =>
-      val fromUri = actor.asInstanceOf[CamelConsumer].endpointUri
-      configure(fromUri, "actor://%s/%s" format (actor.id, actor.uuid))
-      log.debug("registered actor (id=%s, uuid=%s) for consuming messages from %s "
-          format (actor.id, actor.uuid, fromUri))
+      val fromUri = actor.asInstanceOf[Consumer].endpointUri
+      configure(fromUri, "actor:uuid:%s" format actor.uuid)
+      log.debug("registered actor (uuid=%s) for consuming messages from %s "
+          format (actor.uuid, fromUri))
     }
   }
 
@@ -94,6 +92,6 @@ class CamelRouteBuilder extends RouteBuilder with Logging {
     actor.getClass.getAnnotation(classOf[consume]) ne null
 
   private def isConsumerInstance(actor: Actor) =
-    actor.isInstanceOf[CamelConsumer]
+    actor.isInstanceOf[Consumer]
 
 }
diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala
index 3a06d483be..15a8aa523a 100644
--- a/akka-camel/src/test/scala/component/ActorComponentTest.scala
+++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala
@@ -1,11 +1,10 @@
 package se.scalablesolutions.akka.camel.component
 
-import org.apache.camel.{Message, RuntimeCamelException}
+import org.apache.camel.Message
 import org.apache.camel.impl.{SimpleRegistry, DefaultCamelContext}
 import org.junit._
 import org.junit.Assert._
 import org.scalatest.junit.JUnitSuite
-
 import se.scalablesolutions.akka.actor.Actor
 
 /**
@@ -13,45 +12,38 @@ import se.scalablesolutions.akka.actor.Actor
  */
 class ActorComponentTest extends JUnitSuite {
 
-  import ActorComponentTestSetup._
-
-  val actor = ActorComponentTestActor.start
-
-  @Test
-  def testMatchIdOnly() {
-    val result = template.requestBody("actor:%s" format actor.id, "Martin")
-    assertEquals("Hello Martin", result)
-  }
-
-  @Test
-  def testMatchIdAndUuid() {
-    val result = template.requestBody("actor:%s/%s" format (actor.id, actor.uuid), "Martin")
-    assertEquals("Hello Martin", result)
-  }
-
-  @Test
-  def testMatchIdButNotUuid() {
-    intercept[RuntimeCamelException] {
-      template.requestBody("actor:%s/%s" format (actor.id, "wrong"), "Martin")
-    }
-  }
-
-}
-
-object ActorComponentTestActor extends Actor {
-
-  protected def receive = {
-    case msg: Message => reply("Hello %s" format msg.getBody)
-  }
-
-}
-
-object ActorComponentTestSetup {
-
   val context = new DefaultCamelContext(new SimpleRegistry)
   val template = context.createProducerTemplate
 
-  context.start
-  template.start
+  @Before def setUp = {
+    context.start
+    template.start
+  }
 
+  @After def tearDown = {
+    template.stop
+    context.stop
+  }
+
+  @Test def shouldCommunicateWithActorReferencedById = {
+    val actor = new ActorComponentTestActor
+    actor.start
+    assertEquals("Hello Martin", template.requestBody("actor:%s" format actor.getId, "Martin"))
+    assertEquals("Hello Martin", template.requestBody("actor:id:%s" format actor.getId, "Martin"))
+    actor.stop
+  }
+
+  @Test def shouldCommunicateWithActorReferencedByUuid = {
+    val actor = new ActorComponentTestActor
+    actor.start
+    assertEquals("Hello Martin", template.requestBody("actor:uuid:%s" format actor.uuid, "Martin"))
+    actor.stop
+  }
+
+}
+
+class ActorComponentTestActor extends Actor {
+  protected def receive = {
+    case msg: Message => reply("Hello %s" format msg.getBody)
+  }
 }
\ No newline at end of file
diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala
index 8cce9ec2bd..1e00413b14 100644
--- a/akka-camel/src/test/scala/service/CamelServiceTest.scala
+++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala
@@ -3,105 +3,98 @@ package se.scalablesolutions.akka.camel.service
 import org.apache.camel.Message
 import org.apache.camel.builder.RouteBuilder
 import org.apache.camel.impl.DefaultCamelContext
-import org.junit.Test
 import org.junit.Assert._
+import org.junit.{Before, After, Test}
 import org.scalatest.junit.JUnitSuite
 
-import se.scalablesolutions.akka.actor.Actor
 import se.scalablesolutions.akka.annotation.consume
-import se.scalablesolutions.akka.camel.CamelConsumer
-
+import se.scalablesolutions.akka.camel.Consumer
+import se.scalablesolutions.akka.actor.Actor
 
 /**
  * @author Martin Krasser
  */
 class CamelServiceTest extends JUnitSuite {
 
-  import CamelServiceTestSetup._
+  import CamelContextManager.context
 
-  @Test
-  def testActor1() {
+  context = new DefaultCamelContext
+  context.addRoutes(new TestBuilder)
+
+  val template = context.createProducerTemplate
+  var service: CamelService = _
+  var actor1: Actor = _
+  var actor2: Actor = _
+  var actor3: Actor = _
+
+  @Before def setUp = {
+    service = new CamelService {
+      override def onUnload = super.onUnload
+      override def onLoad = super.onLoad
+    }
+
+    actor1 = new TestActor1().start
+    actor2 = new TestActor2().start
+    actor3 = new TestActor3().start
+
+    service.onLoad
+    template.start
+
+  }
+
+  @After def tearDown = {
+    actor1.stop
+    actor2.stop
+    actor3.stop
+
+    template.stop
+    service.onUnload
+  }
+
+  @Test def shouldCommunicateWithAutoDetectedActor1ViaGeneratedRoute = {
     val result = template.requestBody("direct:actor1", "Martin")
     assertEquals("Hello Martin (actor1)", result)
   }
 
-  @Test
-  def testActor2() {
+  @Test def shouldCommunicateWithAutoDetectedActor2ViaGeneratedRoute = {
     val result = template.requestBody("direct:actor2", "Martin")
     assertEquals("Hello Martin (actor2)", result)
   }
 
-  @Test
-  def testActor3() {
+  @Test def shouldCommunicateWithAutoDetectedActor3ViaCustomRoute = {
     val result = template.requestBody("direct:actor3", "Martin")
     assertEquals("Hello Tester (actor3)", result)
   }
 
 }
 
-class TestActor1 extends Actor with CamelConsumer {
-
+class TestActor1 extends Actor with Consumer {
   def endpointUri = "direct:actor1"
 
   protected def receive = {
     case msg: Message => reply("Hello %s (actor1)" format msg.getBody)
   }
-
 }
 
 @consume("direct:actor2")
 class TestActor2 extends Actor {
-
   protected def receive = {
     case msg: Message => reply("Hello %s (actor2)" format msg.getBody)
   }
-
 }
 
 class TestActor3 extends Actor {
+  id = "actor3"
 
   protected def receive = {
     case msg: Message => reply("Hello %s (actor3)" format msg.getBody)
   }
-
 }
 
 class TestBuilder extends RouteBuilder {
-
   def configure {
-    val actorUri = "actor://%s" format classOf[TestActor3].getName
-    from("direct:actor3").transform(constant("Tester")).to(actorUri)
+    val actorUri = "actor:%s" format classOf[TestActor3].getName
+    from("direct:actor3").transform(constant("Tester")).to("actor:actor3")
   }
-
-}
-
-object CamelServiceTestSetup extends CamelService {
-
-  import CamelContextManager.context
-
-  // use a custom camel context
-  context = new DefaultCamelContext
-
-  val template = context.createProducerTemplate
-  var loaded = false
-
-  onLoad
-
-  override def onLoad = {
-    if (!loaded) {
-      // use a custom camel context
-      context.addRoutes(new TestBuilder)
-      // register test actors
-      new TestActor1().start
-      new TestActor2().start
-      new TestActor3().start
-      // start Camel service
-      super.onLoad
-
-      template.start
-      loaded = true
-    }
-  }
-
 }
 
diff --git a/akka-core/src/test/scala/SerializerTest.scala b/akka-core/src/test/scala/SerializerTest.scala
index 889dea4ba8..e11e83a2f5 100644
--- a/akka-core/src/test/scala/SerializerTest.scala
+++ b/akka-core/src/test/scala/SerializerTest.scala
@@ -3,7 +3,7 @@ package se.scalablesolutions.akka.serialization
 import junit.framework.TestCase
 
 import org.scalatest.junit.JUnitSuite
-import org.junit.{Test, Before, After, Ignore}
+import org.junit.{Test, Before, After}
 
 import scala.reflect.BeanInfo
 @BeanInfo 
@@ -18,7 +18,7 @@ case class MyMessage(val id: String, val value: Tuple2[String, Int]) {
 
 
 class SerializerTest extends JUnitSuite {
-  @Test @Ignore // TODO: resolve test failure
+  @Test
   def shouldSerializeString = {
     val f = Foo("debasish")
     val json = Serializer.ScalaJSON.out(f)
@@ -27,7 +27,7 @@ class SerializerTest extends JUnitSuite {
     assert(fo == f)
   }
 
-  @Test @Ignore // TODO: resolve test failure
+  @Test
   def shouldSerializeTuple2 = {
     val message = MyMessage("id", ("hello", 34))
     val json = Serializer.ScalaJSON.out(message)
diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
index fd9b38a3a9..4e07866393 100644
--- a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
+++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
@@ -4,12 +4,12 @@ import org.apache.camel.Message
 
 import se.scalablesolutions.akka.util.Logging
 import se.scalablesolutions.akka.actor.Actor
-import se.scalablesolutions.akka.camel.CamelConsumer
+import se.scalablesolutions.akka.camel.Consumer
 
 /**
  * @author Martin Krasser
  */
-class Consumer1 extends Actor with CamelConsumer with Logging {
+class Consumer1 extends Actor with Consumer with Logging {
 
   def endpointUri = "file:data/input"
 

From 3b62a7a658c1e24d662914eeb03564698cc731f4 Mon Sep 17 00:00:00 2001
From: Martin Krasser 
Date: Mon, 1 Mar 2010 13:35:11 +0100
Subject: [PATCH 08/81] use immutable messages for communication with actors

---
 akka-camel/src/main/scala/Message.scala       | 61 +++++++++++++++++++
 .../main/scala/component/ActorComponent.scala | 20 +++---
 akka-camel/src/test/scala/MessageTest.scala   | 24 ++++++++
 .../scala/component/ActorComponentTest.scala  | 25 ++++----
 .../scala/component/ActorProducerTest.scala   | 40 ++++++++++++
 .../test/scala/service/CamelServiceTest.scala | 18 +++---
 .../src/main/scala/Consumer1.scala            |  6 +-
 .../src/main/scala/Consumer2.scala            |  5 +-
 8 files changed, 163 insertions(+), 36 deletions(-)
 create mode 100644 akka-camel/src/main/scala/Message.scala
 create mode 100644 akka-camel/src/test/scala/MessageTest.scala
 create mode 100644 akka-camel/src/test/scala/component/ActorProducerTest.scala

diff --git a/akka-camel/src/main/scala/Message.scala b/akka-camel/src/main/scala/Message.scala
new file mode 100644
index 0000000000..88f810e045
--- /dev/null
+++ b/akka-camel/src/main/scala/Message.scala
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB 
+ */
+
+package se.scalablesolutions.akka.camel
+
+import org.apache.camel.{Message => CamelMessage}
+import org.apache.camel.impl.DefaultCamelContext
+
+import scala.collection.jcl.{Map => MapWrapper}
+
+/**
+ * @author Martin Krasser
+ */
+class Message(val body: Any, val headers: Map[String, Any]) {
+
+  def this(body: Any) = this(body, Map.empty)
+
+  def bodyAs[T](clazz: Class[T]): T = Message.converter.mandatoryConvertTo[T](clazz, body)
+
+}
+
+/**
+ * @author Martin Krasser
+ */
+object Message {
+
+  val converter = new DefaultCamelContext().getTypeConverter
+
+  def apply(body: Any) = new Message(body)
+
+  def apply(body: Any, headers: Map[String, Any]) = new Message(body, headers)
+
+  def apply(cm: CamelMessage) =
+    new Message(cm.getBody, Map.empty ++ MapWrapper[String, AnyRef](cm.getHeaders).elements)
+
+}
+
+/**
+ * @author Martin Krasser
+ */
+class CamelMessageWrapper(val cm: CamelMessage) {
+
+  def from(m: Message): CamelMessage = {
+    cm.setBody(m.body)
+    for (h <- m.headers) {
+      cm.getHeaders.put(h._1, h._2.asInstanceOf[AnyRef])
+    }
+    cm
+  }
+
+}
+
+/**
+ * @author Martin Krasser
+ */
+object CamelMessageWrapper {
+
+  implicit def wrapCamelMessage(cm: CamelMessage): CamelMessageWrapper = new CamelMessageWrapper(cm)
+
+}
\ No newline at end of file
diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala
index b85cf4829f..d59a4261c9 100644
--- a/akka-camel/src/main/scala/component/ActorComponent.scala
+++ b/akka-camel/src/main/scala/component/ActorComponent.scala
@@ -11,6 +11,7 @@ import org.apache.camel.{Exchange, Consumer, Processor}
 import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent}
 
 import se.scalablesolutions.akka.actor.{ActorRegistry, Actor}
+import se.scalablesolutions.akka.camel.{CamelMessageWrapper, Message}
 
 /**
  * Camel component for interacting with actors.
@@ -104,25 +105,26 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) {
   }
 
   protected def processInOnly(exchange: Exchange, actor: Actor) {
-    actor ! exchange.getIn
+    actor ! Message(exchange.getIn)
   }
 
   protected def processInOut(exchange: Exchange, actor: Actor) {
-    val outmsg = exchange.getOut
+
+    import CamelMessageWrapper._
+
     // TODO: make timeout configurable
-    // TODO: send immutable message
     // TODO: support asynchronous communication
     //       - jetty component: jetty continuations
     //       - file component: completion callbacks
-    val result: Any = actor !! exchange.getIn
+    val result: Any = actor !! Message(exchange.getIn)
 
     result match {
-      case Some((body, headers:Map[String, Any])) => {
-        outmsg.setBody(body)
-        for (header <- headers)
-          outmsg.getHeaders.put(header._1, header._2.asInstanceOf[AnyRef])
+      case Some(m:Message) => {
+        exchange.getOut.from(m)
+      }
+      case Some(body) => {
+        exchange.getOut.setBody(body)
       }
-      case Some(body) => outmsg.setBody(body)
     }
   }
 
diff --git a/akka-camel/src/test/scala/MessageTest.scala b/akka-camel/src/test/scala/MessageTest.scala
new file mode 100644
index 0000000000..791f243ee4
--- /dev/null
+++ b/akka-camel/src/test/scala/MessageTest.scala
@@ -0,0 +1,24 @@
+package se.scalablesolutions.akka.camel.service
+
+import java.io.InputStream
+
+import org.apache.camel.NoTypeConversionAvailableException
+import org.junit.Assert._
+import org.junit.Test
+import org.scalatest.junit.JUnitSuite
+
+import se.scalablesolutions.akka.camel.Message
+
+class MessageTest extends JUnitSuite {
+
+  @Test def shouldConvertDoubleBodyToString = {
+    assertEquals("1.4", new Message(1.4, null).bodyAs(classOf[String]))
+  }
+
+  @Test def shouldThrowExceptionWhenConvertingDoubleBodyToInputStream {
+    intercept[NoTypeConversionAvailableException] {
+      new Message(1.4, null).bodyAs(classOf[InputStream])
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala
index 15a8aa523a..30ea7d1a5b 100644
--- a/akka-camel/src/test/scala/component/ActorComponentTest.scala
+++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala
@@ -1,11 +1,12 @@
 package se.scalablesolutions.akka.camel.component
 
-import org.apache.camel.Message
-import org.apache.camel.impl.{SimpleRegistry, DefaultCamelContext}
 import org.junit._
 import org.junit.Assert._
 import org.scalatest.junit.JUnitSuite
 import se.scalablesolutions.akka.actor.Actor
+import se.scalablesolutions.akka.camel.Message
+import org.apache.camel.{CamelContext, ExchangePattern}
+import org.apache.camel.impl.{DefaultExchange, SimpleRegistry, DefaultCamelContext}
 
 /**
  * @author Martin Krasser
@@ -25,25 +26,27 @@ class ActorComponentTest extends JUnitSuite {
     context.stop
   }
 
-  @Test def shouldCommunicateWithActorReferencedById = {
-    val actor = new ActorComponentTestActor
+  @Test def shouldReceiveResponseFromActorReferencedById = {
+    val actor = new TestActor
     actor.start
     assertEquals("Hello Martin", template.requestBody("actor:%s" format actor.getId, "Martin"))
     assertEquals("Hello Martin", template.requestBody("actor:id:%s" format actor.getId, "Martin"))
     actor.stop
   }
 
-  @Test def shouldCommunicateWithActorReferencedByUuid = {
-    val actor = new ActorComponentTestActor
+  @Test def shouldReceiveResponseFromActorReferencedByUuid = {
+    val actor = new TestActor
     actor.start
     assertEquals("Hello Martin", template.requestBody("actor:uuid:%s" format actor.uuid, "Martin"))
     actor.stop
   }
 
+  class TestActor extends Actor {
+    protected def receive = {
+      case msg: Message => reply("Hello %s" format msg.body)
+    }
+  }
+
 }
 
-class ActorComponentTestActor extends Actor {
-  protected def receive = {
-    case msg: Message => reply("Hello %s" format msg.getBody)
-  }
-}
\ No newline at end of file
+
diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala
new file mode 100644
index 0000000000..73e51ebb04
--- /dev/null
+++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala
@@ -0,0 +1,40 @@
+package se.scalablesolutions.akka.camel.component
+
+import org.apache.camel.{CamelContext, ExchangePattern}
+import org.junit.Assert._
+import org.junit.Test
+import org.scalatest.junit.JUnitSuite
+
+import se.scalablesolutions.akka.actor.Actor
+import se.scalablesolutions.akka.camel.Message
+import org.apache.camel.impl.{DefaultCamelContext, DefaultExchange}
+
+/**
+ * @author Martin Krasser
+ */
+class ActorProducerTest extends JUnitSuite {
+
+  val context = new DefaultCamelContext
+  val endpoint = context.getEndpoint("actor:%s" format classOf[TestActor].getName)
+  val producer = endpoint.createProducer
+
+  @Test def shouldSendAndReceiveMessageBodyAndHeaders = {
+    val exchange = new DefaultExchange(null.asInstanceOf[CamelContext], ExchangePattern.InOut)
+    val actor = new TestActor
+    actor.start
+    exchange.getIn.setBody("Martin")
+    exchange.getIn.setHeader("k1", "v1")
+    producer.process(exchange)
+    assertEquals("Hello Martin", exchange.getOut.getBody)
+    assertEquals("v1", exchange.getOut.getHeader("k1"))
+    assertEquals("v2", exchange.getOut.getHeader("k2"))
+    actor.stop
+  }
+
+  class TestActor extends Actor {
+    protected def receive = {
+      case msg: Message => reply(Message("Hello %s" format msg.body, Map("k2" -> "v2") ++ msg.headers))
+    }
+  }
+
+}
diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala
index 1e00413b14..52f6d1fd04 100644
--- a/akka-camel/src/test/scala/service/CamelServiceTest.scala
+++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala
@@ -1,15 +1,14 @@
 package se.scalablesolutions.akka.camel.service
 
-import org.apache.camel.Message
 import org.apache.camel.builder.RouteBuilder
 import org.apache.camel.impl.DefaultCamelContext
 import org.junit.Assert._
 import org.junit.{Before, After, Test}
 import org.scalatest.junit.JUnitSuite
 
-import se.scalablesolutions.akka.annotation.consume
-import se.scalablesolutions.akka.camel.Consumer
 import se.scalablesolutions.akka.actor.Actor
+import se.scalablesolutions.akka.annotation.consume
+import se.scalablesolutions.akka.camel.{Message, Consumer}
 
 /**
  * @author Martin Krasser
@@ -51,17 +50,17 @@ class CamelServiceTest extends JUnitSuite {
     service.onUnload
   }
 
-  @Test def shouldCommunicateWithAutoDetectedActor1ViaGeneratedRoute = {
+  @Test def shouldReceiveResponseFromActor1ViaGeneratedRoute = {
     val result = template.requestBody("direct:actor1", "Martin")
     assertEquals("Hello Martin (actor1)", result)
   }
 
-  @Test def shouldCommunicateWithAutoDetectedActor2ViaGeneratedRoute = {
+  @Test def shouldReceiveResponseFromActor2ViaGeneratedRoute = {
     val result = template.requestBody("direct:actor2", "Martin")
     assertEquals("Hello Martin (actor2)", result)
   }
 
-  @Test def shouldCommunicateWithAutoDetectedActor3ViaCustomRoute = {
+  @Test def shouldReceiveResponseFromActor3ViaCustomRoute = {
     val result = template.requestBody("direct:actor3", "Martin")
     assertEquals("Hello Tester (actor3)", result)
   }
@@ -72,14 +71,15 @@ class TestActor1 extends Actor with Consumer {
   def endpointUri = "direct:actor1"
 
   protected def receive = {
-    case msg: Message => reply("Hello %s (actor1)" format msg.getBody)
+    case msg: Message => reply("Hello %s (actor1)" format msg.body)
   }
+
 }
 
 @consume("direct:actor2")
 class TestActor2 extends Actor {
   protected def receive = {
-    case msg: Message => reply("Hello %s (actor2)" format msg.getBody)
+    case msg: Message => reply("Hello %s (actor2)" format msg.body)
   }
 }
 
@@ -87,7 +87,7 @@ class TestActor3 extends Actor {
   id = "actor3"
 
   protected def receive = {
-    case msg: Message => reply("Hello %s (actor3)" format msg.getBody)
+    case msg: Message => reply("Hello %s (actor3)" format msg.body)
   }
 }
 
diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
index 4e07866393..b292d6e186 100644
--- a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
+++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala
@@ -1,10 +1,8 @@
 package sample.camel
 
-import org.apache.camel.Message
-
 import se.scalablesolutions.akka.util.Logging
 import se.scalablesolutions.akka.actor.Actor
-import se.scalablesolutions.akka.camel.Consumer
+import se.scalablesolutions.akka.camel.{Message, Consumer}
 
 /**
  * @author Martin Krasser
@@ -14,7 +12,7 @@ class Consumer1 extends Actor with Consumer with Logging {
   def endpointUri = "file:data/input"
 
   def receive = {
-    case msg: Message => log.info("received %s" format msg.getBody(classOf[String]))
+    case msg: Message => log.info("received %s" format msg.bodyAs(classOf[String]))
   }
 
 }
\ No newline at end of file
diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala
index aa9cd5e612..4940c46f0d 100644
--- a/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala
+++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala
@@ -1,9 +1,8 @@
 package sample.camel
 
-import org.apache.camel.Message
-
 import se.scalablesolutions.akka.actor.Actor
 import se.scalablesolutions.akka.annotation.consume
+import se.scalablesolutions.akka.camel.Message
 
 /**
  * @author Martin Krasser
@@ -12,7 +11,7 @@ import se.scalablesolutions.akka.annotation.consume
 class Consumer2 extends Actor {
 
   def receive = {
-    case msg: Message => reply("Hello %s" format msg.getBody(classOf[String]))
+    case msg: Message => reply("Hello %s" format msg.bodyAs(classOf[String]))
   }
 
 }
\ No newline at end of file

From f78b253fbe193546d04268eb6c85dda04739c407 Mon Sep 17 00:00:00 2001
From: Jan Van Besien 
Date: Thu, 4 Mar 2010 12:36:57 +0100
Subject: [PATCH 09/81] Improved event driven dispatcher by not scheduling a
 task for dispatching when another is already busy.

---
 akka-core/src/main/scala/actor/Actor.scala    |  6 ++
 .../ExecutorBasedEventDrivenDispatcher.scala  |  4 +-
 ...BasedEventDrivenDispatcherActorsTest.scala | 89 +++++++++++++++++++
 3 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala

diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala
index e5423e7bd1..077ea02a14 100644
--- a/akka-core/src/main/scala/actor/Actor.scala
+++ b/akka-core/src/main/scala/actor/Actor.scala
@@ -21,6 +21,7 @@ import org.multiverse.api.ThreadLocalTransaction._
 import java.util.{Queue, HashSet}
 import java.util.concurrent.ConcurrentLinkedQueue
 import java.net.InetSocketAddress
+import java.util.concurrent.locks.{Lock, ReentrantLock}
 
 /**
  * Implements the Transactor abstraction. E.g. a transactional actor.
@@ -219,6 +220,11 @@ trait Actor extends TransactionManagement {
   private[akka] var _replyToAddress: Option[InetSocketAddress] = None
   private[akka] val _mailbox: Queue[MessageInvocation] = new ConcurrentLinkedQueue[MessageInvocation]
 
+  /**
+   * This lock ensures thread safety in the dispatching: only one message can be dispatched at once on the actor.
+   */
+  private[akka] val _dispatcherLock:Lock = new ReentrantLock
+
   // ====================================
   // protected fields
   // ====================================
diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
index e115800d4b..6dc1dd03b2 100644
--- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
+++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
@@ -62,7 +62,9 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche
   def dispatch(invocation: MessageInvocation) = if (active) {
     executor.execute(new Runnable() {
       def run = {
-        invocation.receiver.synchronized {
+        val lockedForDispatching = invocation.receiver._dispatcherLock.tryLock
+        if (lockedForDispatching) {
+          // Only dispatch if we got the lock. Otherwise another thread is already dispatching.
           var messageInvocation = invocation.receiver._mailbox.poll
           while (messageInvocation != null) {
             messageInvocation.invoke
diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala
new file mode 100644
index 0000000000..92dc9ebf13
--- /dev/null
+++ b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala
@@ -0,0 +1,89 @@
+package se.scalablesolutions.akka.actor
+
+import org.scalatest.junit.JUnitSuite
+import org.junit.Test
+import se.scalablesolutions.akka.dispatch.Dispatchers
+import org.scalatest.matchers.MustMatchers
+import java.util.concurrent.CountDownLatch
+
+/**
+ * Tests the behaviour of the executor based event driven dispatcher when multiple actors are being dispatched on it.
+ *
+ * @author Jan Van Besien
+ */
+class ExecutorBasedEventDrivenDispatcherActorsTest extends JUnitSuite with MustMatchers with ActorTestUtil {
+  class SlowActor(finishedCounter: CountDownLatch) extends Actor {
+    messageDispatcher = Dispatchers.globalExecutorBasedEventDrivenDispatcher
+    id = "SlowActor"
+
+    def receive = {
+      case x: Int => {
+        Thread.sleep(50) // slow actor
+        finishedCounter.countDown
+        println("s processed " + x)
+      }
+    }
+  }
+
+  class FastActor(finishedCounter: CountDownLatch) extends Actor {
+    messageDispatcher = Dispatchers.globalExecutorBasedEventDrivenDispatcher
+    id = "FastActor"
+
+    def receive = {
+      case x: Int => {
+        finishedCounter.countDown
+        println("f processed " + x)
+      }
+    }
+  }
+
+  @Test def slowActorShouldntBlockFastActor = verify(new TestActor {
+    def test = {
+      val sFinished = new CountDownLatch(50)
+      val fFinished = new CountDownLatch(10)
+      val s = new SlowActor(sFinished)
+      val f = new FastActor(fFinished)
+
+      handle(s, f) {
+        // send a lot of stuff to s
+        for (i <- 1 to 50) {
+          s ! i
+        }
+
+        // send some messages to f
+        for (i <- 1 to 10) {
+          f ! i
+        }
+
+        // now assert that f is finished while s is still busy
+        fFinished.await
+        assert(sFinished.getCount > 0)
+      }
+    }
+  })
+
+}
+
+trait ActorTestUtil {
+  def handle[T](actors: Actor*)(test: => T): T = {
+    for (a <- actors) a.start
+    try
+    {
+      test
+    }
+    finally
+      {
+        for (a <- actors) a.stop
+      }
+  }
+
+  def verify(actor: TestActor): Unit = handle(actor)
+            {actor.test}
+}
+
+abstract class TestActor extends Actor with ActorTestUtil
+{
+  def test: Unit
+
+  def receive = {case _ =>}
+}

From 1e7a6c08385144d78312b0fb84b4b3ce5e670f0a Mon Sep 17 00:00:00 2001
From: Jan Van Besien 
Date: Thu, 4 Mar 2010 13:00:04 +0100
Subject: [PATCH 10/81] Release the lock when done dispatching.

---
 .../ExecutorBasedEventDrivenDispatcher.scala     | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
index 6dc1dd03b2..b4f6c3425c 100644
--- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
+++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
@@ -63,13 +63,17 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche
     executor.execute(new Runnable() {
       def run = {
         val lockedForDispatching = invocation.receiver._dispatcherLock.tryLock
-        if (lockedForDispatching) {
-          // Only dispatch if we got the lock. Otherwise another thread is already dispatching.
-          var messageInvocation = invocation.receiver._mailbox.poll
-          while (messageInvocation != null) {
-            messageInvocation.invoke
-            messageInvocation = invocation.receiver._mailbox.poll
+        try {
+          if (lockedForDispatching) {
+            // Only dispatch if we got the lock. Otherwise another thread is already dispatching.
+            var messageInvocation = invocation.receiver._mailbox.poll
+            while (messageInvocation != null) {
+              messageInvocation.invoke
+              messageInvocation = invocation.receiver._mailbox.poll
+            }
           }
+        } finally {
+          invocation.receiver._dispatcherLock.unlock
         }
       }
     })

From 3693ac6ec893cb6e95a370f68461d6172e1dcf7b Mon Sep 17 00:00:00 2001
From: Jan Van Besien 
Date: Thu, 4 Mar 2010 14:50:11 +0100
Subject: [PATCH 11/81] remove println's in test

---
 .../scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala    | 2 --
 1 file changed, 2 deletions(-)

diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala
index 92dc9ebf13..55e30fec5d 100644
--- a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala
+++ b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorsTest.scala
@@ -20,7 +20,6 @@ class ExecutorBasedEventDrivenDispatcherActorsTest extends JUnitSuite with MustM
       case x: Int => {
         Thread.sleep(50) // slow actor
         finishedCounter.countDown
-        println("s processed " + x)
       }
     }
   }
@@ -32,7 +31,6 @@ class ExecutorBasedEventDrivenDispatcherActorsTest extends JUnitSuite with MustM
     def receive = {
       case x: Int => {
         finishedCounter.countDown
-        println("f processed " + x)
       }
     }
   }

From 4fa0b5f839a1d306101f391eed9b40caf9c6e12b Mon Sep 17 00:00:00 2001
From: Jan Van Besien 
Date: Thu, 4 Mar 2010 15:20:52 +0100
Subject: [PATCH 12/81] only unlock if locked.

---
 .../ExecutorBasedEventDrivenDispatcher.scala         | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
index b4f6c3425c..77879e0e3b 100644
--- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
+++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala
@@ -57,23 +57,23 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche
   @volatile private var active: Boolean = false
   
   val name: String = "event-driven:executor:dispatcher:" + _name
-  init 
-    
+  init
+
   def dispatch(invocation: MessageInvocation) = if (active) {
     executor.execute(new Runnable() {
       def run = {
         val lockedForDispatching = invocation.receiver._dispatcherLock.tryLock
-        try {
-          if (lockedForDispatching) {
+        if (lockedForDispatching) {
+          try {
             // Only dispatch if we got the lock. Otherwise another thread is already dispatching.
             var messageInvocation = invocation.receiver._mailbox.poll
             while (messageInvocation != null) {
               messageInvocation.invoke
               messageInvocation = invocation.receiver._mailbox.poll
             }
+          } finally {
+            invocation.receiver._dispatcherLock.unlock
           }
-        } finally {
-          invocation.receiver._dispatcherLock.unlock
         }
       }
     })

From dc884020f33ebfa4aba81654e1e6c03068ea370b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= 
Date: Thu, 4 Mar 2010 19:02:23 +0100
Subject: [PATCH 13/81] Redis tests now passes with new STM + misc minor
 changes to Cluster

---
 akka-core/src/main/scala/actor/Actor.scala    |   4 +-
 .../actor/BootableActorLoaderService.scala    |   4 +-
 .../remote/BootableRemoteActorService.scala   |   1 -
 akka-core/src/main/scala/remote/Cluster.scala |  33 +++---
 akka-kernel/src/main/scala/Kernel.scala       |   2 +-
 akka-patterns/src/main/scala/Patterns.scala   |  62 ++++++-----
 .../src/main/scala/Storage.scala              |   5 +-
 .../src/main/scala/RedisStorageBackend.scala  | 103 ++++++++++--------
 .../test/scala/RedisPersistentActorSpec.scala |   6 +-
 akka.iml                                      |   6 +
 config/akka-reference.conf                    |   1 +
 11 files changed, 131 insertions(+), 96 deletions(-)

diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala
index df536fb57e..a6b46d903d 100644
--- a/akka-core/src/main/scala/actor/Actor.scala
+++ b/akka-core/src/main/scala/actor/Actor.scala
@@ -309,9 +309,9 @@ trait Actor extends TransactionManagement {
    * If 'trapExit' is set for the actor to act as supervisor, then a faultHandler must be defined.
    * Can be one of:
    * 
-   *  AllForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int)
+   *  faultHandler = Some(AllForOneStrategy(maxNrOfRetries, withinTimeRange))
    *
-   *  OneForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int)
+   *  faultHandler = Some(OneForOneStrategy(maxNrOfRetries, withinTimeRange))
    * 
*/ protected var faultHandler: Option[FaultHandlingStrategy] = None diff --git a/akka-core/src/main/scala/actor/BootableActorLoaderService.scala b/akka-core/src/main/scala/actor/BootableActorLoaderService.scala index 1bacbf6f59..0c84d0965a 100644 --- a/akka-core/src/main/scala/actor/BootableActorLoaderService.scala +++ b/akka-core/src/main/scala/actor/BootableActorLoaderService.scala @@ -31,10 +31,10 @@ trait BootableActorLoaderService extends Bootable with Logging { val toDeploy = for (f <- DEPLOY_DIR.listFiles().toArray.toList.asInstanceOf[List[File]]) yield f.toURL log.info("Deploying applications from [%s]: [%s]", DEPLOY, toDeploy.toArray.toList) new URLClassLoader(toDeploy.toArray, ClassLoader.getSystemClassLoader) - } else if (getClass.getClassLoader.getResourceAsStream("akka.conf") ne null) { + } else if (getClass.getClassLoader.getResourceAsStream("aop.xml") ne null) { getClass.getClassLoader } else throw new IllegalStateException( - "AKKA_HOME is not defined and no 'akka.conf' can be found on the classpath, aborting") + "AKKA_HOME is not defined and akka-.jar can not be found on the classpath; aborting...") ) } diff --git a/akka-core/src/main/scala/remote/BootableRemoteActorService.scala b/akka-core/src/main/scala/remote/BootableRemoteActorService.scala index 429fdb61ec..1b56dcea5a 100644 --- a/akka-core/src/main/scala/remote/BootableRemoteActorService.scala +++ b/akka-core/src/main/scala/remote/BootableRemoteActorService.scala @@ -24,7 +24,6 @@ trait BootableRemoteActorService extends Bootable with Logging { abstract override def onLoad = { if(config.getBool("akka.remote.server.service", true)){ - log.info("Starting up Cluster Service") Cluster.start super.onLoad //Initialize BootableActorLoaderService before remote service log.info("Initializing Remote Actors Service...") diff --git a/akka-core/src/main/scala/remote/Cluster.scala b/akka-core/src/main/scala/remote/Cluster.scala index c2e9069a01..294ce5bd94 100644 --- a/akka-core/src/main/scala/remote/Cluster.scala +++ b/akka-core/src/main/scala/remote/Cluster.scala @@ -60,19 +60,18 @@ private[remote] object ClusterActor { abstract class BasicClusterActor extends ClusterActor { import ClusterActor._ - case class Message(sender : ADDR_T,msg : Array[Byte]) + case class Message(sender : ADDR_T, msg : Array[Byte]) case object PapersPlease extends ClusterMessage case class Papers(addresses: List[RemoteAddress]) extends ClusterMessage case object Block extends ClusterMessage case object Unblock extends ClusterMessage - case class View(othersPresent : Set[ADDR_T]) extends ClusterMessage + case class View(othersPresent: Set[ADDR_T]) extends ClusterMessage case class Zombie(address: ADDR_T) extends ClusterMessage case class RegisterLocalNode(server: RemoteAddress) extends ClusterMessage case class DeregisterLocalNode(server: RemoteAddress) extends ClusterMessage type ADDR_T - @volatile private var local: Node = Node(Nil) @volatile private var remotes: Map[ADDR_T, Node] = Map() @@ -206,17 +205,21 @@ abstract class BasicClusterActor extends ClusterActor { * Loads a specified ClusterActor and delegates to that instance. */ object Cluster extends Cluster with Logging { + lazy val DEFAULT_SERIALIZER_CLASS_NAME = Serializer.Java.getClass.getName + @volatile private[remote] var clusterActor: Option[ClusterActor] = None @volatile private[remote] var supervisor: Option[Supervisor] = None + + // FIXME Use the supervisor member field - private[remote] lazy val serializer: Serializer = { - val className = config.getString("akka.remote.cluster.serializer", Serializer.Java.getClass.getName) - Class.forName(className).newInstance.asInstanceOf[Serializer] - } + private[remote] lazy val serializer: Serializer = + Class.forName(config.getString("akka.remote.cluster.serializer", DEFAULT_SERIALIZER_CLASS_NAME)) + .newInstance.asInstanceOf[Serializer] - private[remote] def createClusterActor : Option[ClusterActor] = { + private[remote] def createClusterActor: Option[ClusterActor] = { val name = config.getString("akka.remote.cluster.actor") - + if (name.isEmpty) throw new IllegalArgumentException( + "Can't start cluster since the 'akka.remote.cluster.actor' configuration option is not defined") try { name map { fqn => val a = Class.forName(fqn).newInstance.asInstanceOf[ClusterActor] @@ -225,7 +228,7 @@ object Cluster extends Cluster with Logging { } } catch { - case e => log.error(e,"Couldn't load Cluster provider: [%s]",name.getOrElse("Not specified")); None + case e => log.error(e, "Couldn't load Cluster provider: [%s]", name.getOrElse("Not specified")); None } } @@ -250,10 +253,11 @@ object Cluster extends Cluster with Logging { def relayMessage(to: Class[_ <: Actor], msg: AnyRef): Unit = clusterActor.foreach(_.relayMessage(to, msg)) - def foreach(f : (RemoteAddress) => Unit) : Unit = clusterActor.foreach(_.foreach(f)) + def foreach(f: (RemoteAddress) => Unit) : Unit = clusterActor.foreach(_.foreach(f)) - def start : Unit = synchronized { - if(supervisor.isEmpty) { + def start: Unit = synchronized { + log.info("Starting up Cluster Service...") + if (supervisor.isEmpty) { for(actor <- createClusterActor; sup <- createSupervisor(actor)) { clusterActor = Some(actor) @@ -262,7 +266,8 @@ object Cluster extends Cluster with Logging { } } - def shutdown : Unit = synchronized { + def shutdown: Unit = synchronized { + log.info("Shutting down Cluster Service...") supervisor.foreach(_.stop) supervisor = None clusterActor = None diff --git a/akka-kernel/src/main/scala/Kernel.scala b/akka-kernel/src/main/scala/Kernel.scala index f63a50a0a7..7b2b2e4693 100644 --- a/akka-kernel/src/main/scala/Kernel.scala +++ b/akka-kernel/src/main/scala/Kernel.scala @@ -79,7 +79,7 @@ object Kernel extends Logging { (____ /__|_ \__|_ \(____ / \/ \/ \/ \/ """) - log.info(" Running version %s", Config.VERSION) + log.info(" Running version %s", Config.VERSION) log.info("==============================") } } diff --git a/akka-patterns/src/main/scala/Patterns.scala b/akka-patterns/src/main/scala/Patterns.scala index b967c07df7..9b7e55ccc9 100644 --- a/akka-patterns/src/main/scala/Patterns.scala +++ b/akka-patterns/src/main/scala/Patterns.scala @@ -3,14 +3,14 @@ package se.scalablesolutions.akka.actor.patterns import se.scalablesolutions.akka.actor.Actor object Patterns { - type PF[A,B] = PartialFunction[A,B] + type PF[A, B] = PartialFunction[A, B] /** * Creates a new PartialFunction whose isDefinedAt is a combination * of the two parameters, and whose apply is first to call filter.apply and then filtered.apply */ - def filter[A,B](filter : PF[A,Unit],filtered : PF[A,B]) : PF[A,B] = { - case a : A if filtered.isDefinedAt(a) && filter.isDefinedAt(a) => + def filter[A, B](filter: PF[A, Unit], filtered: PF[A, B]): PF[A, B] = { + case a: A if filtered.isDefinedAt(a) && filter.isDefinedAt(a) => filter(a) filtered(a) } @@ -18,39 +18,42 @@ object Patterns { /** * Interceptor is a filter(x,y) where x.isDefinedAt is considered to be always true */ - def intercept[A,B](interceptor : (A) => Unit, interceptee : PF[A,B]) : PF[A,B] = filter( - { case a if a.isInstanceOf[A] => interceptor(a) }, - interceptee + def intercept[A, B](interceptor: (A) => Unit, interceptee: PF[A, B]): PF[A, B] = filter( + {case a if a.isInstanceOf[A] => interceptor(a)}, + interceptee ) - + //FIXME 2.8, use default params with CyclicIterator - def loadBalancerActor(actors : => InfiniteIterator[Actor]) : Actor = new Actor with LoadBalancer { + def loadBalancerActor(actors: => InfiniteIterator[Actor]): Actor = new Actor with LoadBalancer { val seq = actors } - def dispatcherActor(routing : PF[Any,Actor], msgTransformer : (Any) => Any) : Actor = new Actor with Dispatcher { - override def transform(msg : Any) = msgTransformer(msg) + def dispatcherActor(routing: PF[Any, Actor], msgTransformer: (Any) => Any): Actor = new Actor with Dispatcher { + override def transform(msg: Any) = msgTransformer(msg) + def routes = routing } - - def dispatcherActor(routing : PF[Any,Actor]) : Actor = new Actor with Dispatcher { - def routes = routing + + def dispatcherActor(routing: PF[Any, Actor]): Actor = new Actor with Dispatcher { + def routes = routing } - def loggerActor(actorToLog : Actor, logger : (Any) => Unit) : Actor = dispatcherActor ( - { case _ => actorToLog }, + def loggerActor(actorToLog: Actor, logger: (Any) => Unit): Actor = dispatcherActor( + {case _ => actorToLog}, logger - ) + ) } -trait Dispatcher { self : Actor => +trait Dispatcher { + self: Actor => - protected def transform(msg : Any) : Any = msg - protected def routes : PartialFunction[Any,Actor] - - protected def dispatch : PartialFunction[Any,Unit] = { + protected def transform(msg: Any): Any = msg + + protected def routes: PartialFunction[Any, Actor] + + protected def dispatch: PartialFunction[Any, Unit] = { case a if routes.isDefinedAt(a) => { - if(self.sender.isDefined) + if (self.sender.isDefined) routes(a) forward transform(a) else routes(a) send transform(a) @@ -60,19 +63,22 @@ trait Dispatcher { self : Actor => def receive = dispatch } -trait LoadBalancer extends Dispatcher { self : Actor => - protected def seq : InfiniteIterator[Actor] +trait LoadBalancer extends Dispatcher { + self: Actor => + protected def seq: InfiniteIterator[Actor] - protected def routes = { case x if seq.hasNext => seq.next } + protected def routes = {case x if seq.hasNext => seq.next} } trait InfiniteIterator[T] extends Iterator[T] -class CyclicIterator[T](items : List[T]) extends InfiniteIterator[T] { - @volatile private[this] var current : List[T] = items +class CyclicIterator[T](items: List[T]) extends InfiniteIterator[T] { + @volatile private[this] var current: List[T] = items + def hasNext = items != Nil + def next = { - val nc = if(current == Nil) items else current + val nc = if (current == Nil) items else current current = nc.tail nc.head } diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala index b4ea8fc381..f52841b817 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala @@ -8,10 +8,11 @@ import se.scalablesolutions.akka.stm.TransactionManagement.transaction import se.scalablesolutions.akka.collection._ import se.scalablesolutions.akka.util.Logging -import org.codehaus.aspectwerkz.proxy.Uuid - +// FIXME move to 'stm' package + add message with more info class NoTransactionInScopeException extends RuntimeException +class StorageException(message: String) extends RuntimeException(message) + /** * Example Scala usage. *

diff --git a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala index 48945d6b8c..be214087f3 100644 --- a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala +++ b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala @@ -72,11 +72,11 @@ private [akka] object RedisStorageBackend extends * base64(T1):base64("debasish.programming_language") -> "scala" * */ - def insertMapStorageEntryFor(name: String, key: Array[Byte], value: Array[Byte]) { + def insertMapStorageEntryFor(name: String, key: Array[Byte], value: Array[Byte]): Unit = withErrorHandling { insertMapStorageEntriesFor(name, List((key, value))) } - def insertMapStorageEntriesFor(name: String, entries: List[Tuple2[Array[Byte], Array[Byte]]]) { + def insertMapStorageEntriesFor(name: String, entries: List[Tuple2[Array[Byte], Array[Byte]]]): Unit = withErrorHandling { mset(entries.map(e => (makeRedisKey(name, e._1), new String(e._2)))) } @@ -89,22 +89,22 @@ private [akka] object RedisStorageBackend extends *

  • : is chosen since it cannot appear in base64 encoding charset
  • *
  • both parts of the key need to be based64 encoded since there can be spaces within each of them
  • */ - private [this] def makeRedisKey(name: String, key: Array[Byte]): String = { + private [this] def makeRedisKey(name: String, key: Array[Byte]): String = withErrorHandling { "%s:%s".format(new String(encode(name.getBytes)), new String(encode(key))) } - private [this] def makeKeyFromRedisKey(redisKey: String) = { + private [this] def makeKeyFromRedisKey(redisKey: String) = withErrorHandling { val nk = redisKey.split(':').map{e: String => decode(e.getBytes)} (nk(0), nk(1)) } - private [this] def mset(entries: List[(String, String)]) { + private [this] def mset(entries: List[(String, String)]): Unit = withErrorHandling { entries.foreach {e: (String, String) => db.set(e._1, e._2) } } - def removeMapStorageFor(name: String): Unit = { + def removeMapStorageFor(name: String): Unit = withErrorHandling { db.keys("%s:*".format(encode(name.getBytes))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -113,18 +113,19 @@ private [akka] object RedisStorageBackend extends } } - def removeMapStorageFor(name: String, key: Array[Byte]): Unit = { + def removeMapStorageFor(name: String, key: Array[Byte]): Unit = withErrorHandling { db.delete(makeRedisKey(name, key)) } - def getMapStorageEntryFor(name: String, key: Array[Byte]): Option[Array[Byte]] = + def getMapStorageEntryFor(name: String, key: Array[Byte]): Option[Array[Byte]] = withErrorHandling { db.get(makeRedisKey(name, key)) match { case None => throw new Predef.NoSuchElementException(new String(key) + " not present") case Some(s) => Some(s.getBytes) } + } - def getMapStorageSizeFor(name: String): Int = { + def getMapStorageSizeFor(name: String): Int = withErrorHandling { db.keys("%s:*".format(new String(encode(name.getBytes)))) match { case None => 0 case Some(keys) => @@ -132,7 +133,7 @@ private [akka] object RedisStorageBackend extends } } - def getMapStorageFor(name: String): List[(Array[Byte], Array[Byte])] = { + def getMapStorageFor(name: String): List[(Array[Byte], Array[Byte])] = withErrorHandling { db.keys("%s:*".format(new String(encode(name.getBytes)))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -143,7 +144,7 @@ private [akka] object RedisStorageBackend extends def getMapStorageRangeFor(name: String, start: Option[Array[Byte]], finish: Option[Array[Byte]], - count: Int): List[(Array[Byte], Array[Byte])] = { + count: Int): List[(Array[Byte], Array[Byte])] = withErrorHandling { import scala.collection.immutable.TreeMap val wholeSorted = @@ -188,19 +189,19 @@ private [akka] object RedisStorageBackend extends } } - def insertVectorStorageEntryFor(name: String, element: Array[Byte]) { + def insertVectorStorageEntryFor(name: String, element: Array[Byte]): Unit = withErrorHandling { db.lpush(new String(encode(name.getBytes)), new String(element)) } - def insertVectorStorageEntriesFor(name: String, elements: List[Array[Byte]]) { + def insertVectorStorageEntriesFor(name: String, elements: List[Array[Byte]]): Unit = withErrorHandling { elements.foreach(insertVectorStorageEntryFor(name, _)) } - def updateVectorStorageEntryFor(name: String, index: Int, elem: Array[Byte]) { + def updateVectorStorageEntryFor(name: String, index: Int, elem: Array[Byte]): Unit = withErrorHandling { db.lset(new String(encode(name.getBytes)), index, new String(elem)) } - def getVectorStorageEntryFor(name: String, index: Int): Array[Byte] = { + def getVectorStorageEntryFor(name: String, index: Int): Array[Byte] = withErrorHandling { db.lindex(new String(encode(name.getBytes)), index) match { case None => throw new Predef.NoSuchElementException(name + " does not have element at " + index) @@ -208,7 +209,7 @@ private [akka] object RedisStorageBackend extends } } - def getVectorStorageRangeFor(name: String, start: Option[Int], finish: Option[Int], count: Int): List[Array[Byte]] = { + def getVectorStorageRangeFor(name: String, start: Option[Int], finish: Option[Int], count: Int): List[Array[Byte]] = withErrorHandling { /** * count is the max number of results to return. Start with * start or 0 (if start is not defined) and go until @@ -237,11 +238,11 @@ private [akka] object RedisStorageBackend extends } } - def insertRefStorageFor(name: String, element: Array[Byte]) { + def insertRefStorageFor(name: String, element: Array[Byte]): Unit = withErrorHandling { db.set(new String(encode(name.getBytes)), new String(element)) } - def getRefStorageFor(name: String): Option[Array[Byte]] = { + def getRefStorageFor(name: String): Option[Array[Byte]] = withErrorHandling { db.get(new String(encode(name.getBytes))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -250,12 +251,13 @@ private [akka] object RedisStorageBackend extends } // add to the end of the queue - def enqueue(name: String, item: Array[Byte]): Boolean = { + def enqueue(name: String, item: Array[Byte]): Boolean = withErrorHandling { db.rpush(new String(encode(name.getBytes)), new String(item)) } + // pop from the front of the queue - def dequeue(name: String): Option[Array[Byte]] = { + def dequeue(name: String): Option[Array[Byte]] = withErrorHandling { db.lpop(new String(encode(name.getBytes))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -265,7 +267,7 @@ private [akka] object RedisStorageBackend extends } // get the size of the queue - def size(name: String): Int = { + def size(name: String): Int = withErrorHandling { db.llen(new String(encode(name.getBytes))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -275,26 +277,28 @@ private [akka] object RedisStorageBackend extends // return an array of items currently stored in the queue // start is the item to begin, count is how many items to return - def peek(name: String, start: Int, count: Int): List[Array[Byte]] = count match { - case 1 => - db.lindex(new String(encode(name.getBytes)), start) match { - case None => - throw new Predef.NoSuchElementException("No element at " + start) - case Some(s) => - List(s.getBytes) - } - case n => - db.lrange(new String(encode(name.getBytes)), start, start + count - 1) match { - case None => - throw new Predef.NoSuchElementException( - "No element found between " + start + " and " + (start + count - 1)) - case Some(es) => - es.map(_.get.getBytes) - } + def peek(name: String, start: Int, count: Int): List[Array[Byte]] = withErrorHandling { + count match { + case 1 => + db.lindex(new String(encode(name.getBytes)), start) match { + case None => + throw new Predef.NoSuchElementException("No element at " + start) + case Some(s) => + List(s.getBytes) + } + case n => + db.lrange(new String(encode(name.getBytes)), start, start + count - 1) match { + case None => + throw new Predef.NoSuchElementException( + "No element found between " + start + " and " + (start + count - 1)) + case Some(es) => + es.map(_.get.getBytes) + } + } } // completely delete the queue - def remove(name: String): Boolean = { + def remove(name: String): Boolean = withErrorHandling { db.delete(new String(encode(name.getBytes))) match { case Some(1) => true case _ => false @@ -302,7 +306,7 @@ private [akka] object RedisStorageBackend extends } // add item to sorted set identified by name - def zadd(name: String, zscore: String, item: Array[Byte]): Boolean = { + def zadd(name: String, zscore: String, item: Array[Byte]): Boolean = withErrorHandling { db.zadd(new String(encode(name.getBytes)), zscore, new String(item)) match { case Some(1) => true case _ => false @@ -310,7 +314,7 @@ private [akka] object RedisStorageBackend extends } // remove item from sorted set identified by name - def zrem(name: String, item: Array[Byte]): Boolean = { + def zrem(name: String, item: Array[Byte]): Boolean = withErrorHandling { db.zrem(new String(encode(name.getBytes)), new String(item)) match { case Some(1) => true case _ => false @@ -318,7 +322,7 @@ private [akka] object RedisStorageBackend extends } // cardinality of the set identified by name - def zcard(name: String): Int = { + def zcard(name: String): Int = withErrorHandling { db.zcard(new String(encode(name.getBytes))) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -326,7 +330,7 @@ private [akka] object RedisStorageBackend extends } } - def zscore(name: String, item: Array[Byte]): String = { + def zscore(name: String, item: Array[Byte]): String = withErrorHandling { db.zscore(new String(encode(name.getBytes)), new String(item)) match { case None => throw new Predef.NoSuchElementException(new String(item) + " not present") @@ -334,7 +338,7 @@ private [akka] object RedisStorageBackend extends } } - def zrange(name: String, start: Int, end: Int): List[Array[Byte]] = { + def zrange(name: String, start: Int, end: Int): List[Array[Byte]] = withErrorHandling { db.zrange(new String(encode(name.getBytes)), start.toString, end.toString, RedisClient.ASC, false) match { case None => throw new Predef.NoSuchElementException(name + " not present") @@ -343,5 +347,16 @@ private [akka] object RedisStorageBackend extends } } - def flushDB = db.flushDb + def flushDB = withErrorHandling(db.flushDb) + + private def withErrorHandling[T](body: => T): T = { + try { + body + } catch { + case e: java.lang.NullPointerException => + throw new StorageException("Could not connect to Redis server") + case e => + throw new StorageException("Error in Redis: " + e.getMessage) + } + } } diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala index 86d4384b70..8c91f0ff61 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala @@ -29,6 +29,7 @@ case object LogSize class AccountActor extends Transactor { private lazy val accountState = RedisStorage.newMap private lazy val txnLog = RedisStorage.newVector + //timeout = 5000 def receive = { // check balance @@ -86,6 +87,7 @@ class AccountActor extends Transactor { } @serializable class PersistentFailerActor extends Transactor { + //timeout = 5000 def receive = { case "Failure" => throw new RuntimeException("expected") @@ -138,7 +140,7 @@ class RedisPersistentActorSpec extends TestCase { bactor.start bactor !! Credit("a-123", 5000) - assertEquals(BigInt(5000), (bactor !! Balance("a-123")).get) + assertEquals(BigInt(5000), (bactor !! (Balance("a-123"), 5000)).get) val failer = new PersistentFailerActor failer.start @@ -147,7 +149,7 @@ class RedisPersistentActorSpec extends TestCase { fail("should throw exception") } catch { case e: RuntimeException => {}} - assertEquals(BigInt(5000), (bactor !! Balance("a-123")).get) + assertEquals(BigInt(5000), (bactor !! (Balance("a-123"), 5000)).get) // should not count the failed one assertEquals(3, (bactor !! LogSize).get) diff --git a/akka.iml b/akka.iml index a39c87020f..74542e8e48 100644 --- a/akka.iml +++ b/akka.iml @@ -15,6 +15,12 @@
    + + + + + +
    diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 296b06428b..5a1c33497b 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -49,6 +49,7 @@ zlib-compression-level = 6 # Options: 0-9 (1 being fastest and 9 being the most compressed), default is 6 + service = on # FIXME add 'service = on' for name = "default" # The name of the cluster #actor = "se.scalablesolutions.akka.remote.JGroupsClusterActor" # FQN of an implementation of ClusterActor serializer = "se.scalablesolutions.akka.serialization.Serializer$Java" # FQN of the serializer class From 81f5f8f07ebbbf661f313c8bb7e0edb1536d0dec Mon Sep 17 00:00:00 2001 From: peter hausel Date: Mon, 1 Mar 2010 01:38:23 -0500 Subject: [PATCH 14/81] initial sbt support --- .gitignore | 3 ++ akka-core/project/build.properties | 7 +++++ akka-core/project/build/AkkaCore.scala | 29 +++++++++++++++++++ akka-util-java/project/build.properties | 7 +++++ .../project/build/AkkaJavaUtilProject.scala | 18 ++++++++++++ akka-util/project/build.properties | 7 +++++ akka-util/project/build/AkkaUtilProject.scala | 17 +++++++++++ project/build.properties | 7 +++++ project/build/AkkaParent.scala | 7 +++++ 9 files changed, 102 insertions(+) create mode 100644 akka-core/project/build.properties create mode 100644 akka-core/project/build/AkkaCore.scala create mode 100644 akka-util-java/project/build.properties create mode 100644 akka-util-java/project/build/AkkaJavaUtilProject.scala create mode 100644 akka-util/project/build.properties create mode 100644 akka-util/project/build/AkkaUtilProject.scala create mode 100644 project/build.properties create mode 100644 project/build/AkkaParent.scala diff --git a/.gitignore b/.gitignore index 22379bef4c..d716ab14ea 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ *~ *# +project/build/target +project/boot +lib_managed etags TAGS reports diff --git a/akka-core/project/build.properties b/akka-core/project/build.properties new file mode 100644 index 0000000000..7338d79aa0 --- /dev/null +++ b/akka-core/project/build.properties @@ -0,0 +1,7 @@ +project.organization=se.scalablesolutions.akka +project.name=akka-core +project.version=0.7-SNAPSHOT +scala.version=2.7.7 +sbt.version=0.7.1 +def.scala.version=2.7.7 +build.scala.versions=2.7.7 diff --git a/akka-core/project/build/AkkaCore.scala b/akka-core/project/build/AkkaCore.scala new file mode 100644 index 0000000000..709ab6b2f6 --- /dev/null +++ b/akka-core/project/build/AkkaCore.scala @@ -0,0 +1,29 @@ +import sbt._ + +class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { + + val akkautil = "se.scalablesolutions.akka" % "akka-util" % "0.7-SNAPSHOT" % "compile" + val akkautiljava = "se.scalablesolutions.akka" % "akka-util-java" % "0.7-SNAPSHOT" % "compile" + + val akka_databinder = "DataBinder" at "http://databinder.net/repo" + val akka_multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" + val akka_jBoss = "jBoss" at "http://repository.jboss.org/maven2" + + val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val commonsio = "commons-io" % "commons-io" % "1.4" % "compile" + val netdatabinder = "net.databinder" % "dispatch-json_2.7.7" % "0.6.4" % "compile" + val netdatabinderhttp = "net.databinder" % "dispatch-http_2.7.7" % "0.6.4" % "compile" + val sbinary = "sbinary" % "sbinary" % "0.3" % "compile" + val jack = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" + val jackcore = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" + val volde = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" + val scalajavautil = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" + val netty = "org.jboss.netty" % "netty" % "3.2.0.ALPHA3" % "compile" + + val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val junit = "junit" % "junit" % "4.5" % "test" + override def packageDocsJar = defaultJarPath("-javadoc.jar") + override def packageSrcJar= defaultJarPath("-sources.jar") + +} diff --git a/akka-util-java/project/build.properties b/akka-util-java/project/build.properties new file mode 100644 index 0000000000..f9ec63abf2 --- /dev/null +++ b/akka-util-java/project/build.properties @@ -0,0 +1,7 @@ +project.organization=se.scalablesolutions.akka +project.name=akka-util-java +project.version=0.7-SNAPSHOT +scala.version=2.7.7 +sbt.version=0.7.1 +def.scala.version=2.7.7 +build.scala.versions=2.7.7 diff --git a/akka-util-java/project/build/AkkaJavaUtilProject.scala b/akka-util-java/project/build/AkkaJavaUtilProject.scala new file mode 100644 index 0000000000..08308f5984 --- /dev/null +++ b/akka-util-java/project/build/AkkaJavaUtilProject.scala @@ -0,0 +1,18 @@ +import sbt._ + +class AkkaJavaUtilProject(info: ProjectInfo) extends DefaultProject(info) { + + val akka_databinder = "DataBinder" at "http://databinder.net/repo" + val akka_configgy = "Configgy" at "http://www.lag.net/repo" + val akka_multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" + val akka_jBoss = "jBoss" at "http://repository.jboss.org/maven2" + val guiceyfruit = "GuiceyFruit" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" + + val guicey = "org.guiceyfruit" % "guice-core" % "2.0-beta-4" % "compile" + val proto = "com.google.protobuf" % "protobuf-java" % "2.2.0" % "compile" + val multi = "org.multiverse" % "multiverse-alpha" % "0.3" % "compile" + + override def packageDocsJar = defaultJarPath("-javadoc.jar") + override def packageSrcJar= defaultJarPath("-sources.jar") + +} diff --git a/akka-util/project/build.properties b/akka-util/project/build.properties new file mode 100644 index 0000000000..a25350528e --- /dev/null +++ b/akka-util/project/build.properties @@ -0,0 +1,7 @@ +project.organization=se.scalablesolutions.akka +project.name=akka-util +project.version=0.7-SNAPSHOT +scala.version=2.7.7 +sbt.version=0.7.1 +def.scala.version=2.7.7 +build.scala.versions=2.7.7 diff --git a/akka-util/project/build/AkkaUtilProject.scala b/akka-util/project/build/AkkaUtilProject.scala new file mode 100644 index 0000000000..a7ca8a7c01 --- /dev/null +++ b/akka-util/project/build/AkkaUtilProject.scala @@ -0,0 +1,17 @@ +import sbt._ + +class AkkaUtilProject(info: ProjectInfo) extends DefaultProject(info) { + + val akka_databinder = "DataBinder" at "http://databinder.net/repo" + val akka_configgy = "Configgy" at "http://www.lag.net/repo" + val akka_multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" + val akka_jBoss = "jBoss" at "http://repository.jboss.org/maven2" + + val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val configgy = "net.lag" % "configgy" % "1.4.7" % "compile" + + override def packageDocsJar = defaultJarPath("-javadoc.jar") + override def packageSrcJar= defaultJarPath("-sources.jar") + +} diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000000..9f7e717580 --- /dev/null +++ b/project/build.properties @@ -0,0 +1,7 @@ +project.organization=se.scalablesolutions.akka +project.name=akka +project.version=0.7-SNAPSHOT +scala.version=2.7.7 +sbt.version=0.7.1 +def.scala.version=2.7.7 +build.scala.versions=2.7.7 diff --git a/project/build/AkkaParent.scala b/project/build/AkkaParent.scala new file mode 100644 index 0000000000..db0f68f5c2 --- /dev/null +++ b/project/build/AkkaParent.scala @@ -0,0 +1,7 @@ +import sbt._ + +class AkkaParent(info: ProjectInfo) extends ParentProject(info) { + lazy val javautil = project("akka-util-java", "akka java util") + lazy val util = project("akka-util", "akka util") + lazy val core = project("akka-core", "akka core", util,javautil) +} From 927edd970ea1d3753bb8d532014a24efd3b8ace4 Mon Sep 17 00:00:00 2001 From: peter hausel Date: Tue, 2 Mar 2010 00:30:14 -0500 Subject: [PATCH 15/81] second phase --- .gitignore | 4 +- akka-core/project/build.properties | 7 --- akka-core/project/build/AkkaCore.scala | 29 ------------- akka-persistence/pom.xml | 43 ------------------- akka-util/project/build.properties | 7 --- akka-util/project/build/AkkaUtilProject.scala | 17 -------- project/build/AkkaParent.scala | 7 --- 7 files changed, 2 insertions(+), 112 deletions(-) delete mode 100644 akka-core/project/build.properties delete mode 100644 akka-core/project/build/AkkaCore.scala delete mode 100644 akka-persistence/pom.xml delete mode 100644 akka-util/project/build.properties delete mode 100644 akka-util/project/build/AkkaUtilProject.scala delete mode 100644 project/build/AkkaParent.scala diff --git a/.gitignore b/.gitignore index d716ab14ea..9ae8cf1ee8 100755 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ *~ *# -project/build/target -project/boot +*/project/build/target +*/project/boot lib_managed etags TAGS diff --git a/akka-core/project/build.properties b/akka-core/project/build.properties deleted file mode 100644 index 7338d79aa0..0000000000 --- a/akka-core/project/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -project.organization=se.scalablesolutions.akka -project.name=akka-core -project.version=0.7-SNAPSHOT -scala.version=2.7.7 -sbt.version=0.7.1 -def.scala.version=2.7.7 -build.scala.versions=2.7.7 diff --git a/akka-core/project/build/AkkaCore.scala b/akka-core/project/build/AkkaCore.scala deleted file mode 100644 index 709ab6b2f6..0000000000 --- a/akka-core/project/build/AkkaCore.scala +++ /dev/null @@ -1,29 +0,0 @@ -import sbt._ - -class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { - - val akkautil = "se.scalablesolutions.akka" % "akka-util" % "0.7-SNAPSHOT" % "compile" - val akkautiljava = "se.scalablesolutions.akka" % "akka-util-java" % "0.7-SNAPSHOT" % "compile" - - val akka_databinder = "DataBinder" at "http://databinder.net/repo" - val akka_multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" - val akka_jBoss = "jBoss" at "http://repository.jboss.org/maven2" - - val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" - val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" - val commonsio = "commons-io" % "commons-io" % "1.4" % "compile" - val netdatabinder = "net.databinder" % "dispatch-json_2.7.7" % "0.6.4" % "compile" - val netdatabinderhttp = "net.databinder" % "dispatch-http_2.7.7" % "0.6.4" % "compile" - val sbinary = "sbinary" % "sbinary" % "0.3" % "compile" - val jack = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" - val jackcore = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" - val volde = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" - val scalajavautil = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" - val netty = "org.jboss.netty" % "netty" % "3.2.0.ALPHA3" % "compile" - - val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" - val junit = "junit" % "junit" % "4.5" % "test" - override def packageDocsJar = defaultJarPath("-javadoc.jar") - override def packageSrcJar= defaultJarPath("-sources.jar") - -} diff --git a/akka-persistence/pom.xml b/akka-persistence/pom.xml deleted file mode 100644 index cad4757353..0000000000 --- a/akka-persistence/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - 4.0.0 - - akka-persistence-parent - Akka Persistence Modules - - pom - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - akka-persistence-common - akka-persistence-redis - akka-persistence-mongo - akka-persistence-cassandra - - - - - akka-core - ${project.groupId} - ${project.version} - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - diff --git a/akka-util/project/build.properties b/akka-util/project/build.properties deleted file mode 100644 index a25350528e..0000000000 --- a/akka-util/project/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -project.organization=se.scalablesolutions.akka -project.name=akka-util -project.version=0.7-SNAPSHOT -scala.version=2.7.7 -sbt.version=0.7.1 -def.scala.version=2.7.7 -build.scala.versions=2.7.7 diff --git a/akka-util/project/build/AkkaUtilProject.scala b/akka-util/project/build/AkkaUtilProject.scala deleted file mode 100644 index a7ca8a7c01..0000000000 --- a/akka-util/project/build/AkkaUtilProject.scala +++ /dev/null @@ -1,17 +0,0 @@ -import sbt._ - -class AkkaUtilProject(info: ProjectInfo) extends DefaultProject(info) { - - val akka_databinder = "DataBinder" at "http://databinder.net/repo" - val akka_configgy = "Configgy" at "http://www.lag.net/repo" - val akka_multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" - val akka_jBoss = "jBoss" at "http://repository.jboss.org/maven2" - - val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" - val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" - val configgy = "net.lag" % "configgy" % "1.4.7" % "compile" - - override def packageDocsJar = defaultJarPath("-javadoc.jar") - override def packageSrcJar= defaultJarPath("-sources.jar") - -} diff --git a/project/build/AkkaParent.scala b/project/build/AkkaParent.scala deleted file mode 100644 index db0f68f5c2..0000000000 --- a/project/build/AkkaParent.scala +++ /dev/null @@ -1,7 +0,0 @@ -import sbt._ - -class AkkaParent(info: ProjectInfo) extends ParentProject(info) { - lazy val javautil = project("akka-util-java", "akka java util") - lazy val util = project("akka-util", "akka util") - lazy val core = project("akka-core", "akka core", util,javautil) -} From 71b82c54488d693769c816cfc11d517ec9ea102d Mon Sep 17 00:00:00 2001 From: peter hausel Date: Tue, 2 Mar 2010 00:34:14 -0500 Subject: [PATCH 16/81] new master parent --- project/build/AkkaProject.scala | 131 ++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 project/build/AkkaProject.scala diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala new file mode 100644 index 0000000000..4eaef68963 --- /dev/null +++ b/project/build/AkkaProject.scala @@ -0,0 +1,131 @@ +import sbt._ + +class AkkaParent(info: ProjectInfo) extends ParentProject(info) { + //repos + val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" + val databinder = "DataBinder" at "http://databinder.net/repo" + val configgy = "Configgy" at "http://www.lag.net/repo" + val multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" + val jboss = "jBoss" at "http://repository.jboss.org/maven2" + val guiceyfruit = "GuiceyFruit" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" + val embeddedrepo = "embedded repo" at "http://guice-maven.googlecode.com/svn/trunk" + val google = "google" at "http://google-maven-repository.googlecode.com/svn/repository" + val m2 = "m2" at "http://download.java.net/maven/2" + + //project versions + val JERSEYVERSION = "1.1.5" + val ATMOVERSION = "0.6-SNAPSHOT" + val CASSANDRAVERSION = "0.5.0" + + //project defintions + lazy val javautil = project("akka-util-java", "akka-java-util", new AkkaJavaUtilProject(_)) + lazy val util = project("akka-util", "akka-util",new AkkaUtilProject(_)) + lazy val core = project("akka-core", "akka-core", new AkkaCoreProject(_), util, javautil) + lazy val amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), core) + lazy val rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), core) + lazy val comet = project("akka-comet", "akka-comet",new AkkaCometProject(_), rest) + lazy val patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), core) + lazy val security = project("akka-security", "akka-security", new AkkaSecurityProject(_), core) + lazy val persitence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) + + // subprojects + class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { + val sjson = "sjson.json" % "sjson" % "0.4" % "compile" + val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val commonsio = "commons-io" % "commons-io" % "1.4" % "compile" + val netdatabinder = "net.databinder" % "dispatch-json_2.7.7" % "0.6.4" % "compile" + val netdatabinderhttp = "net.databinder" % "dispatch-http_2.7.7" % "0.6.4" % "compile" + val sbinary = "sbinary" % "sbinary" % "0.3" % "compile" + val jack = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" + val jackcore = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" + val volde = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" + val scalajavautil = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" + val netty = "org.jboss.netty" % "netty" % "3.2.0.ALPHA3" % "compile" + //testing + val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val junit = "junit" % "junit" % "4.5" % "test" + } + + class AkkaUtilProject(info: ProjectInfo) extends DefaultProject(info) { + val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val configgy = "net.lag" % "configgy" % "1.4.7" % "compile" + } + + class AkkaJavaUtilProject(info: ProjectInfo) extends DefaultProject(info) { + val guicey = "org.guiceyfruit" % "guice-core" % "2.0-beta-4" % "compile" + val proto = "com.google.protobuf" % "protobuf-java" % "2.2.0" % "compile" + val multi = "org.multiverse" % "multiverse-alpha" % "0.3" % "compile" + } + + class AkkaAMQPProject(info:ProjectInfo) extends DefaultProject(info) { + val rabbit = "com.rabbitmq" % "amqp-client" % "1.7.2" + } + + class AkkaRestProject(info:ProjectInfo) extends DefaultProject(info) { + val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" + val jersey = "com.sun.jersey" % "jersey-core" % JERSEYVERSION % "compile" + val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" + val jerseyjson = "com.sun.jersey" % "jersey-json" % JERSEYVERSION % "compile" + val jerseycontrib = "com.sun.jersey.contribs" % "jersey-scala" % JERSEYVERSION % "compile" + val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + } + + class AkkaCometProject(info:ProjectInfo) extends DefaultProject(info) { + val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" + val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" + val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMOVERSION % "compile" + val atmojersey = "org.atmosphere" % "atmosphere-jersey" % ATMOVERSION % "compile" + val atmoruntime = "org.atmosphere" % "atmosphere-runtime" % ATMOVERSION % "compile" + } + + class AkkaPatternsProject(info:ProjectInfo) extends DefaultProject(info) { + //testing + val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val junit = "junit" % "junit" % "4.5" % "test" + } + + class AkkaSecurityProject(info:ProjectInfo) extends DefaultProject(info) { + val annotation = "javax.annotation" % "jsr250-api" % "1.0" + val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" + val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val lift = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" + //testing + val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val junit = "junit" % "junit" % "4.5" % "test" + val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" + } + + + class AkkaPersistenceCommonProject(info:ProjectInfo) extends DefaultProject(info) { + val facebook = "com.facebook" % "thrift" % "1.0" % "compile" + val commonspool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" + } + class AkkaRedisProject(info:ProjectInfo) extends DefaultProject(info) { + val redis = "com.redis" % "redisclient" % "1.0.1" % "compile" + } + + class AkkaMongoProject(info:ProjectInfo) extends DefaultProject(info) { + val mongo = "org.mongodb" % "mongo-java-driver" % "1.1" % "compile" + } + + class AkkaCassandraProject(info:ProjectInfo) extends DefaultProject(info) { + val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRAVERSION % "compile" + val cassandralib = "org.apache.cassandra" % "high-scale-lib" % CASSANDRAVERSION % "test" + val cassandraclhm = "org.apache.cassandra" % "clhm-production" % CASSANDRAVERSION % "test" + val commonscoll = "commons-collections" % "commons-collections" % "3.2.1" % "test" + val googlecoll = "com.google.collections" % "google-collections" % "1.0" % "test" + val slf4j = "org.slf4j" % "slf4j-api" % "1.5.8" % "test" + val slf4jlog4j = "org.slf4j" % "slf4j-log4j12" % "1.5.8" % "test" + val log4j = "log4j" % "log4j" % "1.2.15" % "test" + } + + class AkkaPersistenceParentProject(info:ProjectInfo) extends ParentProject(info) { + lazy val akkapersistencecommon = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_)) + lazy val redis = project("akka-persistence-redis","akka-persistence-redis", new AkkaRedisProject(_),akkapersistencecommon) + lazy val mongo = project("akka-persistence-mongo","akka-persistence-mongo", new AkkaMongoProject(_),akkapersistencecommon) + lazy val cassandra = project("akka-persistence-cassandra","akka-persistence-cassandra", new AkkaCassandraProject(_),akkapersistencecommon) + + } +} From 399ee1da50bf77212afbfe9eb26e7c3b6299153a Mon Sep 17 00:00:00 2001 From: peter hausel Date: Tue, 2 Mar 2010 23:44:16 -0500 Subject: [PATCH 17/81] added remaining projects --- .gitignore | 1 + project/build/AkkaProject.scala | 79 +++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9ae8cf1ee8..69dd6d55c9 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ *# +project/boot/* */project/build/target */project/boot lib_managed diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 4eaef68963..b865af998a 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -26,7 +26,13 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { lazy val comet = project("akka-comet", "akka-comet",new AkkaCometProject(_), rest) lazy val patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), core) lazy val security = project("akka-security", "akka-security", new AkkaSecurityProject(_), core) - lazy val persitence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) + lazy val persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) + lazy val cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) + lazy val kernel = project("akka-kernel","akka-kernel", new AkkaKernelProject(_),core,rest,persistence,cluster,amqp,security,comet) + //examples + lazy val funtest = project("akka-fun-test-java","akka-fun-test-java", new AkkaFunTestProject(_),kernel) + lazy val samples = project("akka-samples","akka-samples", new AkkaSamplesParentProject(_)) + // subprojects class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { @@ -90,7 +96,7 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val annotation = "javax.annotation" % "jsr250-api" % "1.0" val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - val lift = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" + val liftutil = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" //testing val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" @@ -103,7 +109,7 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val commonspool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" } class AkkaRedisProject(info:ProjectInfo) extends DefaultProject(info) { - val redis = "com.redis" % "redisclient" % "1.0.1" % "compile" + val redis = "com.redis" % "redisclient" % "1.1" % "compile" } class AkkaMongoProject(info:ProjectInfo) extends DefaultProject(info) { @@ -118,14 +124,79 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val googlecoll = "com.google.collections" % "google-collections" % "1.0" % "test" val slf4j = "org.slf4j" % "slf4j-api" % "1.5.8" % "test" val slf4jlog4j = "org.slf4j" % "slf4j-log4j12" % "1.5.8" % "test" + val log4j = "log4j" % "log4j" % "1.2.15" % "test" } class AkkaPersistenceParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val akkapersistencecommon = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_)) + lazy val akkapersistencecommon = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_),core) lazy val redis = project("akka-persistence-redis","akka-persistence-redis", new AkkaRedisProject(_),akkapersistencecommon) lazy val mongo = project("akka-persistence-mongo","akka-persistence-mongo", new AkkaMongoProject(_),akkapersistencecommon) lazy val cassandra = project("akka-persistence-cassandra","akka-persistence-cassandra", new AkkaCassandraProject(_),akkapersistencecommon) } + + class AkkaJgroupsProject(info:ProjectInfo) extends DefaultProject(info) { + val jgroups = "jgroups" % "jgroups" % "2.8.0.CR7" % "compile" + } + + class AkkaShoalProject(info:ProjectInfo) extends DefaultProject(info) { + val shoal = "shoal-jxta" % "shoal" % "1.1-20090818" % "compile" + val shoalextra = "shoal-jxta" % "jxta" % "1.1-20090818" % "compile" + } + + class AkkaClusterParentProject(info:ProjectInfo) extends ParentProject(info) { + lazy val jgroups = project("akka-cluster-jgroups","akka-cluster-jgroups", new AkkaJgroupsProject(_),core) + lazy val shoal = project("akka-cluster-shoal","akka-cluster-shoal", new AkkaShoalProject(_),core) + } + + class AkkaKernelProject(info:ProjectInfo) extends DefaultProject(info) { + val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" + val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMOVERSION % "compile" + val atmojersey = "org.atmosphere" % "atmosphere-jersey" % ATMOVERSION % "compile" + val atmoruntime = "org.atmosphere" % "atmosphere-runtime" % ATMOVERSION % "compile" + } + + //examples + class AkkaFunTestProject(info:ProjectInfo) extends DefaultProject(info) { + val protobuf = "com.google.protobuf" % "protobuf-java" % "2.2.0" + val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" + val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" + val jerseyjson = "com.sun.jersey" % "jersey-json" % JERSEYVERSION % "compile" + val jerseyatom = "com.sun.jersey" % "jersey-atom" % JERSEYVERSION % "compile" + //testing + val junit = "junit" % "junit" % "4.5" % "test" + val jmock = "org.jmock" % "jmock" % "2.4.0" % "test" + } + + + class AkkaSampleChatProject(info:ProjectInfo) extends DefaultProject(info) + + class AkkaSampleLiftProject(info:ProjectInfo) extends DefaultProject(info) { + val liftutil = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" + val lift = "net.liftweb" % "lift-webkit" % "1.1-M6" % "compile" + val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" + //testing + val jetty = "org.mortbay.jetty" % "jetty" % "6.1.6" % "test" + val junit = "junit" % "junit" % "4.5" % "test" + } + + class AkkaSampleRestJavaProject(info:ProjectInfo) extends DefaultProject(info) + + class AkkaSampleRestScalaProject(info:ProjectInfo) extends DefaultProject(info) { + val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + } + + class AkkaSampleSecurityProject(info:ProjectInfo) extends DefaultProject(info) { + val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val annotation = "javax.annotation" % "jsr250-api" % "1.0" + } + + class AkkaSamplesParentProject(info:ProjectInfo) extends ParentProject(info) { + lazy val chat = project("akka-sample-chat","akka-sample-chat",new AkkaSampleChatProject(_),kernel) + lazy val lift = project("akka-sample-lift","akka-sample-lift",new AkkaSampleLiftProject(_),kernel) + lazy val restjava = project("akka-sample-rest-java","akka-sample-rest-java",new AkkaSampleRestJavaProject(_),kernel) + lazy val restscala = project("akka-sample-rest-scala","akka-sample-rest-scala",new AkkaSampleRestScalaProject(_),kernel) + lazy val security = project("akka-sample-security","akka-sample-security",new AkkaSampleSecurityProject(_),kernel) + } } From 53d9158825ae95221de4f8635ad80c22a0d3d671 Mon Sep 17 00:00:00 2001 From: peter hausel Date: Tue, 2 Mar 2010 23:45:26 -0500 Subject: [PATCH 18/81] remove pom files --- akka-amqp/pom.xml | 29 - akka-cluster/akka-cluster-jgroups/pom.xml | 24 - akka-cluster/akka-cluster-shoal/pom.xml | 34 -- akka-cluster/akka-cluster-tribes/pom.xml | 24 - akka-cluster/pom.xml | 42 -- akka-comet/pom.xml | 54 -- akka-core/pom.xml | 111 ---- akka-fun-test-java/pom.xml | 123 ---- akka-kernel/pom.xml | 136 ----- akka-patterns/pom.xml | 39 -- .../akka-persistence-cassandra/pom.xml | 86 --- .../akka-persistence-common/pom.xml | 29 - .../akka-persistence-mongo/pom.xml | 31 - .../akka-persistence-redis/pom.xml | 31 - akka-rest/pom.xml | 57 -- akka-samples/akka-sample-chat/pom.xml | 38 -- akka-samples/akka-sample-lift/pom.xml | 50 -- akka-samples/akka-sample-rest-java/pom.xml | 49 -- akka-samples/akka-sample-rest-scala/pom.xml | 46 -- akka-samples/akka-sample-security/pom.xml | 52 -- akka-samples/pom.xml | 56 -- akka-security/pom.xml | 63 -- akka-util-java/pom.xml | 74 --- akka-util/pom.xml | 39 -- pom.xml | 565 ------------------ 25 files changed, 1882 deletions(-) delete mode 100644 akka-amqp/pom.xml delete mode 100644 akka-cluster/akka-cluster-jgroups/pom.xml delete mode 100644 akka-cluster/akka-cluster-shoal/pom.xml delete mode 100644 akka-cluster/akka-cluster-tribes/pom.xml delete mode 100644 akka-cluster/pom.xml delete mode 100644 akka-comet/pom.xml delete mode 100644 akka-core/pom.xml delete mode 100644 akka-fun-test-java/pom.xml delete mode 100644 akka-kernel/pom.xml delete mode 100644 akka-patterns/pom.xml delete mode 100644 akka-persistence/akka-persistence-cassandra/pom.xml delete mode 100644 akka-persistence/akka-persistence-common/pom.xml delete mode 100644 akka-persistence/akka-persistence-mongo/pom.xml delete mode 100644 akka-persistence/akka-persistence-redis/pom.xml delete mode 100644 akka-rest/pom.xml delete mode 100644 akka-samples/akka-sample-chat/pom.xml delete mode 100644 akka-samples/akka-sample-lift/pom.xml delete mode 100644 akka-samples/akka-sample-rest-java/pom.xml delete mode 100644 akka-samples/akka-sample-rest-scala/pom.xml delete mode 100644 akka-samples/akka-sample-security/pom.xml delete mode 100644 akka-samples/pom.xml delete mode 100644 akka-security/pom.xml delete mode 100644 akka-util-java/pom.xml delete mode 100644 akka-util/pom.xml delete mode 100644 pom.xml diff --git a/akka-amqp/pom.xml b/akka-amqp/pom.xml deleted file mode 100644 index aa569958a6..0000000000 --- a/akka-amqp/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - 4.0.0 - - akka-amqp - Akka AMQP Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-core - ${project.groupId} - ${project.version} - - - com.rabbitmq - amqp-client - 1.7.0 - - - - diff --git a/akka-cluster/akka-cluster-jgroups/pom.xml b/akka-cluster/akka-cluster-jgroups/pom.xml deleted file mode 100644 index 85d25e2330..0000000000 --- a/akka-cluster/akka-cluster-jgroups/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - 4.0.0 - - akka-cluster-jgroups - Akka Cluster JGroups Module - - jar - - - akka-cluster-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - jgroups - jgroups - 2.8.0.CR7 - - - - diff --git a/akka-cluster/akka-cluster-shoal/pom.xml b/akka-cluster/akka-cluster-shoal/pom.xml deleted file mode 100644 index b58e77dcf5..0000000000 --- a/akka-cluster/akka-cluster-shoal/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - 4.0.0 - - akka-cluster-shoal - Akka Cluster Shoal Module - - jar - - - akka-cluster-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - shoal-jxta - shoal - 1.1-20090818 - - - shoal-jxta - jxta - 1.1-20090818 - - - - diff --git a/akka-cluster/akka-cluster-tribes/pom.xml b/akka-cluster/akka-cluster-tribes/pom.xml deleted file mode 100644 index efcea51aa8..0000000000 --- a/akka-cluster/akka-cluster-tribes/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - 4.0.0 - - akka-cluster-tribes - Akka Cluster Tribes Module - - jar - - - akka-cluster-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - org.apache.tomcat - tribes - 6.0.20 - - - - diff --git a/akka-cluster/pom.xml b/akka-cluster/pom.xml deleted file mode 100644 index 9d7bd42000..0000000000 --- a/akka-cluster/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - akka-cluster-parent - Akka Cluster Modules - - pom - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - akka-cluster-jgroups - - akka-cluster-shoal - - - - - akka-core - ${project.groupId} - ${project.version} - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - diff --git a/akka-comet/pom.xml b/akka-comet/pom.xml deleted file mode 100644 index 88cdc0cf57..0000000000 --- a/akka-comet/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - 4.0.0 - - akka-comet - Akka Comet Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-rest - ${project.groupId} - ${project.version} - - - - - com.sun.grizzly - grizzly-comet-webserver - ${grizzly.version} - - - - - javax.servlet - servlet-api - 2.5 - - - org.atmosphere - atmosphere-annotations - ${atmosphere.version} - - - org.atmosphere - atmosphere-jersey - ${atmosphere.version} - - - org.atmosphere - atmosphere-runtime - ${atmosphere.version} - - - diff --git a/akka-core/pom.xml b/akka-core/pom.xml deleted file mode 100644 index d6ca57ebfe..0000000000 --- a/akka-core/pom.xml +++ /dev/null @@ -1,111 +0,0 @@ - - 4.0.0 - - akka-core - Akka Core - Actors, Remote Actors, Transactors and STM Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-util-java - ${project.groupId} - ${project.version} - - - akka-util - ${project.groupId} - ${project.version} - - - org.scala-lang - scala-library - ${scala.version} - - - org.codehaus.aspectwerkz - aspectwerkz-nodeps-jdk5 - 2.1 - - - org.codehaus.aspectwerkz - aspectwerkz-jdk5 - 2.1 - - - org.jboss.netty - netty - 3.2.0.ALPHA3 - - - org.scala-tools - javautils - 2.7.4-0.1 - - - - - voldemort.store.compress - h2-lzf - 1.0 - - - org.codehaus.jackson - jackson-core-asl - 1.2.1 - - - org.codehaus.jackson - jackson-mapper-asl - 1.2.1 - - - sbinary - sbinary - 0.3 - - - net.databinder - dispatch-json_2.7.7 - 0.6.4 - - - commons-io - commons-io - 1.4 - - - net.databinder - dispatch-http_2.7.7 - 0.6.4 - - - sjson.json - sjson - 0.4 - - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - diff --git a/akka-fun-test-java/pom.xml b/akka-fun-test-java/pom.xml deleted file mode 100644 index beb19f25c5..0000000000 --- a/akka-fun-test-java/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ - - 4.0.0 - - Akka Functional Tests in Java - akka-fun-test-java - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-kernel - ${project.groupId} - ${project.version} - - - ${project.groupId} - akka-persistence-cassandra - ${project.version} - - - com.google.protobuf - protobuf-java - 2.2.0 - - - com.sun.grizzly - grizzly-servlet-webserver - ${grizzly.version} - test - - - com.sun.jersey - jersey-server - ${jersey.version} - test - - - com.sun.jersey - jersey-json - ${jersey.version} - test - - - com.sun.jersey - jersey-client - ${jersey.version} - test - - - com.sun.jersey - jersey-atom - ${jersey.version} - test - - - junit - junit - 4.5 - test - - - org.jmock - jmock - 2.4.0 - test - - - - - src/main/java - src/test/java - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - **/* - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/InMemNestedStateTest* - **/*Persistent* - - - - - - - false - src/test/resources - - - false - src/test/java - - ** - - - **/*.java - - - - - - - - diff --git a/akka-kernel/pom.xml b/akka-kernel/pom.xml deleted file mode 100644 index 4b1d114d45..0000000000 --- a/akka-kernel/pom.xml +++ /dev/null @@ -1,136 +0,0 @@ - - 4.0.0 - - akka-kernel - Akka Kernel Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-rest - ${project.groupId} - ${project.version} - - - akka-amqp - ${project.groupId} - ${project.version} - - - akka-security - ${project.groupId} - ${project.version} - - - akka-persistence-cassandra - ${project.groupId} - ${project.version} - - - akka-persistence-mongo - ${project.groupId} - ${project.version} - - - akka-persistence-redis - ${project.groupId} - ${project.version} - - - akka-comet - ${project.groupId} - ${project.version} - - - akka-cluster-jgroups - ${project.groupId} - ${project.version} - - - - - - com.sun.jersey - jersey-server - ${jersey.version} - - - org.atmosphere - atmosphere-annotations - ${atmosphere.version} - - - org.atmosphere - atmosphere-jersey - ${atmosphere.version} - - - org.atmosphere - atmosphere-runtime - ${atmosphere.version} - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 1.2.1 - - - install - - shade - - - - - junit:junit - - - - - - - se.scalablesolutions.akka.Main - - - - - - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - diff --git a/akka-patterns/pom.xml b/akka-patterns/pom.xml deleted file mode 100644 index 6d3f8e0b41..0000000000 --- a/akka-patterns/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - 4.0.0 - - akka-patterns - Akka Patterns Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-core - ${project.groupId} - ${project.version} - - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - diff --git a/akka-persistence/akka-persistence-cassandra/pom.xml b/akka-persistence/akka-persistence-cassandra/pom.xml deleted file mode 100644 index d8490382d5..0000000000 --- a/akka-persistence/akka-persistence-cassandra/pom.xml +++ /dev/null @@ -1,86 +0,0 @@ - - 4.0.0 - - akka-persistence-cassandra - Akka Persistence Cassandra Module - - jar - - - akka-persistence-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-persistence-common - ${project.groupId} - ${project.version} - - - com.google.code.google-collections - google-collect - - - - - - - org.apache.cassandra - cassandra - 0.5.0 - - - org.apache.cassandra - high-scale-lib - 0.5.0 - test - - - org.apache.cassandra - clhm-production - 0.5.0 - test - - - com.google.collections - google-collections - 1.0-rc1 - test - - - commons-collections - commons-collections - 3.2.1 - test - - - commons-lang - commons-lang - 2.4 - test - - - org.slf4j - slf4j-api - 1.5.8 - test - - - org.slf4j - slf4j-log4j12 - 1.5.8 - test - - - - - log4j - log4j - 1.2.13 - - - - diff --git a/akka-persistence/akka-persistence-common/pom.xml b/akka-persistence/akka-persistence-common/pom.xml deleted file mode 100644 index 623fbea571..0000000000 --- a/akka-persistence/akka-persistence-common/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - 4.0.0 - - akka-persistence-common - Akka Persistence Common Module - - jar - - - akka-persistence-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - com.facebook - thrift - 1.0 - - - commons-pool - commons-pool - 1.5.1 - - - - diff --git a/akka-persistence/akka-persistence-mongo/pom.xml b/akka-persistence/akka-persistence-mongo/pom.xml deleted file mode 100644 index 616deb7492..0000000000 --- a/akka-persistence/akka-persistence-mongo/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - akka-persistence-mongo - Akka Persistence Mongo Module - - jar - - - akka-persistence-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-persistence-common - ${project.groupId} - ${project.version} - - - - - org.mongodb - mongo-java-driver - 1.1 - - - - diff --git a/akka-persistence/akka-persistence-redis/pom.xml b/akka-persistence/akka-persistence-redis/pom.xml deleted file mode 100644 index 112d4764cb..0000000000 --- a/akka-persistence/akka-persistence-redis/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - akka-persistence-redis - Akka Persistence Redis Module - - jar - - - akka-persistence-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-persistence-common - ${project.groupId} - ${project.version} - - - - - com.redis - redisclient - 1.1 - - - - diff --git a/akka-rest/pom.xml b/akka-rest/pom.xml deleted file mode 100644 index 4e875cb310..0000000000 --- a/akka-rest/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - 4.0.0 - - akka-rest - Akka REST Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-core - ${project.groupId} - ${project.version} - - - - - javax.servlet - servlet-api - 2.5 - - - com.sun.jersey - jersey-core - ${jersey.version} - - - com.sun.jersey - jersey-server - ${jersey.version} - - - com.sun.jersey - jersey-json - ${jersey.version} - - - javax.ws.rs - jsr311-api - 1.1 - - - com.sun.jersey.contribs - jersey-scala - ${jersey.version} - - - diff --git a/akka-samples/akka-sample-chat/pom.xml b/akka-samples/akka-sample-chat/pom.xml deleted file mode 100644 index 20ee421978..0000000000 --- a/akka-samples/akka-sample-chat/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - 4.0.0 - - akka-sample-chat - Akka Chat Sample Module - - jar - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - src/main/scala - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - diff --git a/akka-samples/akka-sample-lift/pom.xml b/akka-samples/akka-sample-lift/pom.xml deleted file mode 100644 index a07c288e31..0000000000 --- a/akka-samples/akka-sample-lift/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - akka-sample-lift - Akka Lift Sample Module - - war - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - 1.1-M6 - - - - - net.liftweb - lift-util - ${lift.version} - - - net.liftweb - lift-webkit - ${lift.version} - - - javax.servlet - servlet-api - 2.5 - provided - - - junit - junit - 4.5 - test - - - org.mortbay.jetty - jetty - [6.1.6,) - test - - - diff --git a/akka-samples/akka-sample-rest-java/pom.xml b/akka-samples/akka-sample-rest-java/pom.xml deleted file mode 100644 index 6539a0234b..0000000000 --- a/akka-samples/akka-sample-rest-java/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - 4.0.0 - - akka-sample-rest-java - Akka REST Java Sample Module - - jar - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - src/main/java - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - **/* - - - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - diff --git a/akka-samples/akka-sample-rest-scala/pom.xml b/akka-samples/akka-sample-rest-scala/pom.xml deleted file mode 100644 index e62a329f8c..0000000000 --- a/akka-samples/akka-sample-rest-scala/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - 4.0.0 - - akka-sample-rest-scala - Akka REST Scala Sample Module - - jar - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - javax.ws.rs - jsr311-api - 1.0 - - - - - src/main/scala - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - diff --git a/akka-samples/akka-sample-security/pom.xml b/akka-samples/akka-sample-security/pom.xml deleted file mode 100644 index 86f331fd65..0000000000 --- a/akka-samples/akka-sample-security/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - 4.0.0 - - akka-sample-security - Akka Sample Security Module - - jar - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - javax.ws.rs - jsr311-api - 1.0 - - - javax.annotation - jsr250-api - 1.0 - - - - - - src/main/scala - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - diff --git a/akka-samples/pom.xml b/akka-samples/pom.xml deleted file mode 100644 index ad94fc8aab..0000000000 --- a/akka-samples/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - 4.0.0 - - akka-samples-parent - Akka Sample Modules - - pom - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - akka-sample-chat - akka-sample-lift - akka-sample-security - akka-sample-rest-scala - akka-sample-rest-java - - - - - akka-core - ${project.groupId} - ${project.version} - - - akka-persistence-cassandra - ${project.groupId} - ${project.version} - - - akka-persistence-redis - ${project.groupId} - ${project.version} - - - akka-rest - ${project.groupId} - ${project.version} - - - akka-comet - ${project.groupId} - ${project.version} - - - akka-security - ${project.groupId} - ${project.version} - - - diff --git a/akka-security/pom.xml b/akka-security/pom.xml deleted file mode 100644 index 8d7c66a5f7..0000000000 --- a/akka-security/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ - - 4.0.0 - - akka-security - Akka Security Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-core - ${project.groupId} - ${project.version} - - - javax.annotation - jsr250-api - 1.0 - - - com.sun.jersey - jersey-server - 1.1.3-ea - - - javax.ws.rs - jsr311-api - 1.0 - - - net.liftweb - lift-util - 1.1-M6 - - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - org.mockito - mockito-all - 1.8.0 - test - - - diff --git a/akka-util-java/pom.xml b/akka-util-java/pom.xml deleted file mode 100644 index e0a729491b..0000000000 --- a/akka-util-java/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - 4.0.0 - - akka-util-java - Akka Java Utilities Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - org.guiceyfruit - guice-core - 2.0-beta-4 - - - com.google.protobuf - protobuf-java - 2.2.0 - - - org.multiverse - multiverse-alpha - 0.3 - jar-with-dependencies - - - org.multiverse - multiverse-core - - - asm - asm-tree - - - asm - asm-analysis - - - asm - asm-commons - - - asm - asm-util - - - - - - - src/main/java - src/test/java - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - **/* - - - - - - diff --git a/akka-util/pom.xml b/akka-util/pom.xml deleted file mode 100644 index 9b22090ee9..0000000000 --- a/akka-util/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - 4.0.0 - - akka-util - Akka Util Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - org.scala-lang - scala-library - ${scala.version} - - - org.codehaus.aspectwerkz - aspectwerkz-nodeps-jdk5 - 2.1 - - - org.codehaus.aspectwerkz - aspectwerkz-jdk5 - 2.1 - - - net.lag - configgy - 1.4.7 - - - - diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 3cfc2839a8..0000000000 --- a/pom.xml +++ /dev/null @@ -1,565 +0,0 @@ - - - 4.0.0 - - Akka Project - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - 2009 - http://akkasource.org - pom - - - Akka implements a unique hybrid of: - * Actors , which gives you: - * Simple and high-level abstractions for concurrency and parallelism. - * Asynchronous, non-blocking and highly performant event-driven programming model. - * Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM). - * Supervision hierarchies with let-it-crash semantics. For writing highly fault-tolerant systems that never stop, systems that self-heal. - * Software Transactional Memory (STM). (Distributed transactions coming soon). - * Transactors: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic rollback and retry. - * Remoting: highly performant distributed actors with remote supervision and error management. - * Cluster membership management. - - Akka also has a set of add-on modules: - * Persistence: A set of pluggable back-end storage modules that works in sync with the STM. - * Cassandra distributed and highly scalable database. - * MongoDB document database. - * Redis data structures database (upcoming) - * REST (JAX-RS): Expose actors as REST services. - * Comet: Expose actors as Comet services. - * Security: Digest and Kerberos based security. - * Microkernel: Run Akka as a stand-alone kernel. - - - - 2.7.7 - UTF-8 - 1.5 - ${maven.compiler.source} - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - 0.5.2 - 1.1.5 - 1.9.18-i - - - - akka-util-java - akka-util - akka-cluster - akka-core - akka-persistence - akka-rest - akka-comet - akka-amqp - akka-security - akka-patterns - akka-kernel - akka-fun-test-java - akka-samples - - - - Scalable Solutions AB - http://scalablesolutions.se - - - - - The Apache License, ASL Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - - - - - jboner - Jonas Bonér - +1 - jonas [REMOVE] AT jonasboner DOT com - - Founder - Hacker - Despot - - - - viktorklang - Viktor Klang - +1 - viktor.klang [REMOVE] AT gmail DOT com - - Apostle - - - - - - scm:git:git://github.com/jboner/akka.git - scm:git:git@github.com:jboner/akka.git - http://github.com/jboner/akka - - - - assembla - http://assembla.com/spaces/akka/ - - - - hudson - http://hudson.scala-tools.org/job/akka/ - - - - - - - - - User and Developer Discussion List - http://groups.google.com/group/akka-user - akka-user@googlegroups.com - akka-user+subscribe@googlegroups.com - akka-user+unsubscribe@googlegroups.com - - - - - - project.embedded.module - Project Embedded Repository - file://${env.AKKA_HOME}/embedded-repo - - - repo1.maven - Maven Main Repository - http://repo1.maven.org/maven2 - - - scala-tools-snapshots - Scala-Tools Maven2 Snapshot Repository - http://scala-tools.org/repo-snapshots - - - scala-tools - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - - lag - Configgy's' Repository - http://www.lag.net/repo - - - multiverse-releases - http://multiverse.googlecode.com/svn/maven-repository/releases - - false - - - - multiverse-snaphosts - http://multiverse.googlecode.com/svn/maven-repository/snapshots - - - maven2-repository.dev.java.net - Java.net Repository for Maven - http://download.java.net/maven/2 - - - java.net - Java.net Legacy Repository for Maven - http://download.java.net/maven/1 - legacy - - - guiceyfruit.release - GuiceyFruit Release Repository - http://guiceyfruit.googlecode.com/svn/repo/releases/ - - false - - - true - - - - guiceyfruit.snapshot - GuiceyFruit Snapshot Repository - http://guiceyfruit.googlecode.com/svn/repo/snapshots/ - - true - - - false - - - - guice-maven - guice maven - http://guice-maven.googlecode.com/svn/trunk - - - google-maven-repository - Google Maven Repository - http://google-maven-repository.googlecode.com/svn/repository/ - - - repository.codehaus.org - Codehaus Maven Repository - http://repository.codehaus.org - - true - - - - repository.jboss.org - JBoss Repository for Maven - http://repository.jboss.org/maven2 - - false - - - - nexus.griddynamics.net - Grid Dynamics Maven Repository - https://nexus.griddynamics.net/nexus/content/groups/public - - false - - - - databinder.net/repo/ - dbDispatch Repository for Maven - http://databinder.net/repo - - false - - - - - - - onejar-maven-plugin.googlecode.com - http://onejar-maven-plugin.googlecode.com/svn/mavenrepo - - - scala-tools.org - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - - - - src/main/scala - src/test/scala - - - org.apache.maven.plugins - maven-enforcer-plugin - 1.0-beta-1 - - - enforce-akka-home - - enforce - - - - - env.AKKA_HOME - "You must have set AKKA_HOME!" - - - - ${env.AKKA_HOME}/embedded-repo - - - - true - - - - enforce-java - - enforce - - - - - 1.6.0 - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.4.2 - - - **/*Test.java - - - - **/InMemNestedStateTest.java - - - - akka.home - ${basedir}/.. - - - org.multiverse.api.exceptions.WriteConflictException.reuse - true - - - - - - org.mortbay.jetty - maven-jetty-plugin - - / - 5 - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.0.2 - - 1.5 - 1.5 - - - - org.scala-tools - maven-scala-plugin - 2.10.1 - - - - compile - testCompile - - - - - - -Xmx1024m - - - ${scala.version} - - - - true - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-changes-plugin - 2.0 - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - ${project.version} - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 1.0-beta-1 - - - org.apache.felix - maven-bundle-plugin - 2.0.0 - true - - - J2SE-1.5 - <_versionpolicy>[$(@),$(version;=+;$(@))) - - - - - create-bundle - package - - bundle - - - - bundle-install - install - - install - - - - - - - - - - - org.codehaus.mojo - taglist-maven-plugin - 2.3 - - - FIXME - TODO - XXX - @fixme - @todo - @deprecated - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.1.2 - - - - cim - dependencies - dependency-convergence - - index - issue-tracking - license - mailing-list - - plugins - project-team - scm - summary - - - - - - org.scala-tools - maven-scala-plugin - 2.12.2 - - ${project.build.sourceEncoding} - - 1.2-SNAPSHOT - ${scala.version} - - -Xmx1024m - -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties - - - - - - - org.apache.maven.plugins - maven-changes-plugin - 2.1 - - - - changes-report - - - - - ${basedir}/changes.xml - - - - maven-surefire-report-plugin - - - - - report-only - - - - - - - - - release - - - scala-tools.org - http://nexus.scala-tools.org/content/repositories/releases - - - scala-tools.org - file://${user.home}/.m2/mvnsites/akka - - - - - hudson - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/repo-snapshots - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/repo-snapshots - false - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/mvnsites-snapshots/akka - - - - - From 4aabc07be27ade6c746771da3b1eeb4b5f0e4993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 5 Mar 2010 09:17:15 +0100 Subject: [PATCH 19/81] cleaned up buildfile --- project/build/AkkaProject.scala | 166 ++++++++++++++++---------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index b865af998a..f3ef2987de 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -1,7 +1,7 @@ import sbt._ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { - //repos + // repos val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" val databinder = "DataBinder" at "http://databinder.net/repo" val configgy = "Configgy" at "http://www.lag.net/repo" @@ -12,57 +12,58 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val google = "google" at "http://google-maven-repository.googlecode.com/svn/repository" val m2 = "m2" at "http://download.java.net/maven/2" - //project versions - val JERSEYVERSION = "1.1.5" - val ATMOVERSION = "0.6-SNAPSHOT" - val CASSANDRAVERSION = "0.5.0" + // project versions + val JERSEY_VERSION = "1.1.5" + val ATMO_VERSION = "0.6-SNAPSHOT" + val CASSANDRA_VERSION = "0.5.0" - //project defintions - lazy val javautil = project("akka-util-java", "akka-java-util", new AkkaJavaUtilProject(_)) - lazy val util = project("akka-util", "akka-util",new AkkaUtilProject(_)) - lazy val core = project("akka-core", "akka-core", new AkkaCoreProject(_), util, javautil) - lazy val amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), core) - lazy val rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), core) - lazy val comet = project("akka-comet", "akka-comet",new AkkaCometProject(_), rest) - lazy val patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), core) - lazy val security = project("akka-security", "akka-security", new AkkaSecurityProject(_), core) - lazy val persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) - lazy val cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) - lazy val kernel = project("akka-kernel","akka-kernel", new AkkaKernelProject(_),core,rest,persistence,cluster,amqp,security,comet) - //examples - lazy val funtest = project("akka-fun-test-java","akka-fun-test-java", new AkkaFunTestProject(_),kernel) - lazy val samples = project("akka-samples","akka-samples", new AkkaSamplesParentProject(_)) + // project defintions + lazy val akka_java_util = project("akka-util-java", "akka-java-util", new AkkaJavaUtilProject(_)) + lazy val akka_util = project("akka-util", "akka-util",new AkkaUtilProject(_)) + lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_), akka_util, akka_java_util) + lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) + lazy val akka_rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), akka_core) + lazy val akka_comet = project("akka-comet", "akka-comet",new AkkaCometProject(_), akka_rest) + lazy val akka_patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), akka_core) + lazy val akka_security = project("akka-security", "akka-security", new AkkaSecurityProject(_), akka_core) + lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) + lazy val akka_cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) + lazy val akka_kernel = project("akka-kernel","akka-kernel", new AkkaKernelProject(_),akka_core,akka_rest,akka_persistence,akka_cluster,akka_amqp,akka_security,akka_comet) + + // examples + lazy val akka_fun_test = project("akka-fun-test-java","akka-fun-test-java", new AkkaFunTestProject(_),akka_kernel) + lazy val akka_samples = project("akka-samples","akka-samples", new AkkaSamplesParentProject(_)) // subprojects class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { val sjson = "sjson.json" % "sjson" % "0.4" % "compile" - val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" - val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" - val commonsio = "commons-io" % "commons-io" % "1.4" % "compile" - val netdatabinder = "net.databinder" % "dispatch-json_2.7.7" % "0.6.4" % "compile" - val netdatabinderhttp = "net.databinder" % "dispatch-http_2.7.7" % "0.6.4" % "compile" + val werkz = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val werkz_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" + val dispatch_json = "net.databinder" % "dispatch-json_2.7.7" % "0.6.4" % "compile" + val dispatch_http = "net.databinder" % "dispatch-http_2.7.7" % "0.6.4" % "compile" val sbinary = "sbinary" % "sbinary" % "0.3" % "compile" - val jack = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" - val jackcore = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" - val volde = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" - val scalajavautil = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" + val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" + val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" + val voldemort = "voldemort.store.compress" % "h2-lzf" % "1.0" % "compile" + val javautils = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" val netty = "org.jboss.netty" % "netty" % "3.2.0.ALPHA3" % "compile" - //testing + // testing val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" } class AkkaUtilProject(info: ProjectInfo) extends DefaultProject(info) { - val aspec = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" - val apsec_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" + val werkz = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" + val werkz_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" val configgy = "net.lag" % "configgy" % "1.4.7" % "compile" } class AkkaJavaUtilProject(info: ProjectInfo) extends DefaultProject(info) { val guicey = "org.guiceyfruit" % "guice-core" % "2.0-beta-4" % "compile" - val proto = "com.google.protobuf" % "protobuf-java" % "2.2.0" % "compile" - val multi = "org.multiverse" % "multiverse-alpha" % "0.3" % "compile" + val protobuf = "com.google.protobuf" % "protobuf-java" % "2.2.0" % "compile" + val multiverse = "org.multiverse" % "multiverse-alpha" % "0.3" % "compile" } class AkkaAMQPProject(info:ProjectInfo) extends DefaultProject(info) { @@ -71,43 +72,43 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { class AkkaRestProject(info:ProjectInfo) extends DefaultProject(info) { val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" - val jersey = "com.sun.jersey" % "jersey-core" % JERSEYVERSION % "compile" - val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" - val jerseyjson = "com.sun.jersey" % "jersey-json" % JERSEYVERSION % "compile" - val jerseycontrib = "com.sun.jersey.contribs" % "jersey-scala" % JERSEYVERSION % "compile" - val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val jersey = "com.sun.jersey" % "jersey-core" % JERSEY_VERSION % "compile" + val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" + val jersey_json = "com.sun.jersey" % "jersey-json" % JERSEY_VERSION % "compile" + val jersey_contrib = "com.sun.jersey.contribs" % "jersey-scala" % JERSEY_VERSION % "compile" + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" } class AkkaCometProject(info:ProjectInfo) extends DefaultProject(info) { val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" - val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMOVERSION % "compile" - val atmojersey = "org.atmosphere" % "atmosphere-jersey" % ATMOVERSION % "compile" - val atmoruntime = "org.atmosphere" % "atmosphere-runtime" % ATMOVERSION % "compile" + val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" + val atmo_jersey = "org.atmosphere" % "atmosphere-jersey" % ATMO_VERSION % "compile" + val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" } class AkkaPatternsProject(info:ProjectInfo) extends DefaultProject(info) { - //testing + // testing val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" } class AkkaSecurityProject(info:ProjectInfo) extends DefaultProject(info) { val annotation = "javax.annotation" % "jsr250-api" % "1.0" - val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" - val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - val liftutil = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" - //testing + val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" + // testing val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" } - class AkkaPersistenceCommonProject(info:ProjectInfo) extends DefaultProject(info) { - val facebook = "com.facebook" % "thrift" % "1.0" % "compile" - val commonspool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" + val thrift = "com.facebook" % "thrift" % "1.0" % "compile" + val commons_pool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" } + class AkkaRedisProject(info:ProjectInfo) extends DefaultProject(info) { val redis = "com.redis" % "redisclient" % "1.1" % "compile" } @@ -117,22 +118,21 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { } class AkkaCassandraProject(info:ProjectInfo) extends DefaultProject(info) { - val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRAVERSION % "compile" - val cassandralib = "org.apache.cassandra" % "high-scale-lib" % CASSANDRAVERSION % "test" - val cassandraclhm = "org.apache.cassandra" % "clhm-production" % CASSANDRAVERSION % "test" - val commonscoll = "commons-collections" % "commons-collections" % "3.2.1" % "test" - val googlecoll = "com.google.collections" % "google-collections" % "1.0" % "test" + val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRA_VERSION % "compile" + val high_scale = "org.apache.cassandra" % "high-scale-lib" % CASSANDRA_VERSION % "test" + val cassandra_clhm = "org.apache.cassandra" % "clhm-production" % CASSANDRA_VERSION % "test" + val commons_coll = "commons-collections" % "commons-collections" % "3.2.1" % "test" + val google_coll = "com.google.collections" % "google-collections" % "1.0" % "test" val slf4j = "org.slf4j" % "slf4j-api" % "1.5.8" % "test" - val slf4jlog4j = "org.slf4j" % "slf4j-log4j12" % "1.5.8" % "test" - + val slf4j_log4j = "org.slf4j" % "slf4j-log4j12" % "1.5.8" % "test" val log4j = "log4j" % "log4j" % "1.2.15" % "test" } class AkkaPersistenceParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val akkapersistencecommon = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_),core) - lazy val redis = project("akka-persistence-redis","akka-persistence-redis", new AkkaRedisProject(_),akkapersistencecommon) - lazy val mongo = project("akka-persistence-mongo","akka-persistence-mongo", new AkkaMongoProject(_),akkapersistencecommon) - lazy val cassandra = project("akka-persistence-cassandra","akka-persistence-cassandra", new AkkaCassandraProject(_),akkapersistencecommon) + lazy val akka_persistence_common = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_),akka_core) + lazy val akka_persistence_redis = project("akka-persistence-redis","akka-persistence-redis", new AkkaRedisProject(_),akka_persistence_common) + lazy val akka_persistence_mongo = project("akka-persistence-mongo","akka-persistence-mongo", new AkkaMongoProject(_),akka_persistence_common) + lazy val akka_persistence_cassandra = project("akka-persistence-cassandra","akka-persistence-cassandra", new AkkaCassandraProject(_),akka_persistence_common) } @@ -142,29 +142,29 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { class AkkaShoalProject(info:ProjectInfo) extends DefaultProject(info) { val shoal = "shoal-jxta" % "shoal" % "1.1-20090818" % "compile" - val shoalextra = "shoal-jxta" % "jxta" % "1.1-20090818" % "compile" + val shoal_extra = "shoal-jxta" % "jxta" % "1.1-20090818" % "compile" } class AkkaClusterParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val jgroups = project("akka-cluster-jgroups","akka-cluster-jgroups", new AkkaJgroupsProject(_),core) - lazy val shoal = project("akka-cluster-shoal","akka-cluster-shoal", new AkkaShoalProject(_),core) + lazy val akka_cluster_jgroups = project("akka-cluster-jgroups","akka-cluster-jgroups", new AkkaJgroupsProject(_),akka_core) + lazy val akka_cluster_shoal = project("akka-cluster-shoal","akka-cluster-shoal", new AkkaShoalProject(_),akka_core) } class AkkaKernelProject(info:ProjectInfo) extends DefaultProject(info) { - val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" - val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMOVERSION % "compile" - val atmojersey = "org.atmosphere" % "atmosphere-jersey" % ATMOVERSION % "compile" - val atmoruntime = "org.atmosphere" % "atmosphere-runtime" % ATMOVERSION % "compile" + val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" + val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" + val atmo_jersey = "org.atmosphere" % "atmosphere-jersey" % ATMO_VERSION % "compile" + val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" } - //examples + // examples class AkkaFunTestProject(info:ProjectInfo) extends DefaultProject(info) { val protobuf = "com.google.protobuf" % "protobuf-java" % "2.2.0" val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" - val jerseyserver = "com.sun.jersey" % "jersey-server" % JERSEYVERSION % "compile" - val jerseyjson = "com.sun.jersey" % "jersey-json" % JERSEYVERSION % "compile" - val jerseyatom = "com.sun.jersey" % "jersey-atom" % JERSEYVERSION % "compile" - //testing + val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" + val jersey_json = "com.sun.jersey" % "jersey-json" % JERSEY_VERSION % "compile" + val jersey_atom = "com.sun.jersey" % "jersey-atom" % JERSEY_VERSION % "compile" + // testing val junit = "junit" % "junit" % "4.5" % "test" val jmock = "org.jmock" % "jmock" % "2.4.0" % "test" } @@ -173,10 +173,10 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { class AkkaSampleChatProject(info:ProjectInfo) extends DefaultProject(info) class AkkaSampleLiftProject(info:ProjectInfo) extends DefaultProject(info) { - val liftutil = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" val lift = "net.liftweb" % "lift-webkit" % "1.1-M6" % "compile" + val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" - //testing + // testing val jetty = "org.mortbay.jetty" % "jetty" % "6.1.6" % "test" val junit = "junit" % "junit" % "4.5" % "test" } @@ -184,19 +184,19 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { class AkkaSampleRestJavaProject(info:ProjectInfo) extends DefaultProject(info) class AkkaSampleRestScalaProject(info:ProjectInfo) extends DefaultProject(info) { - val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" } class AkkaSampleSecurityProject(info:ProjectInfo) extends DefaultProject(info) { - val jsr = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - val annotation = "javax.annotation" % "jsr250-api" % "1.0" + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" } class AkkaSamplesParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val chat = project("akka-sample-chat","akka-sample-chat",new AkkaSampleChatProject(_),kernel) - lazy val lift = project("akka-sample-lift","akka-sample-lift",new AkkaSampleLiftProject(_),kernel) - lazy val restjava = project("akka-sample-rest-java","akka-sample-rest-java",new AkkaSampleRestJavaProject(_),kernel) - lazy val restscala = project("akka-sample-rest-scala","akka-sample-rest-scala",new AkkaSampleRestScalaProject(_),kernel) - lazy val security = project("akka-sample-security","akka-sample-security",new AkkaSampleSecurityProject(_),kernel) + lazy val akka_sample_chat = project("akka-sample-chat","akka-sample-chat",new AkkaSampleChatProject(_),akka_kernel) + lazy val akka_sample_lift = project("akka-sample-lift","akka-sample-lift",new AkkaSampleLiftProject(_),akka_kernel) + lazy val akka_sample_rest_java = project("akka-sample-rest-java","akka-sample-rest-java",new AkkaSampleRestJavaProject(_),akka_kernel) + lazy val akka_sample_rest_scala = project("akka-sample-rest-scala","akka-sample-rest-scala",new AkkaSampleRestScalaProject(_),akka_kernel) + lazy val akka_sample_security = project("akka-sample-security","akka-sample-security",new AkkaSampleSecurityProject(_),akka_kernel) } } From 9705ee52c5fc830ba670173935de3ebd31f4ec9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 5 Mar 2010 14:33:14 +0100 Subject: [PATCH 20/81] added test filter to filter away all tests that end with Spec --- .../test/scala/RemoteClientShutdownTest.scala | 3 +- project/build/AkkaProject.scala | 167 +++++++++--------- 2 files changed, 88 insertions(+), 82 deletions(-) diff --git a/akka-core/src/test/scala/RemoteClientShutdownTest.scala b/akka-core/src/test/scala/RemoteClientShutdownTest.scala index f6fbea1bb9..d330dce5ce 100644 --- a/akka-core/src/test/scala/RemoteClientShutdownTest.scala +++ b/akka-core/src/test/scala/RemoteClientShutdownTest.scala @@ -6,7 +6,7 @@ import Actor.Sender.Self import org.scalatest.junit.JUnitSuite import org.junit.Test - +/* class RemoteClientShutdownTest extends JUnitSuite { @Test def shouldShutdownRemoteClient = { RemoteNode.start("localhost", 9999) @@ -28,3 +28,4 @@ class TravelingActor extends RemoteActor("localhost", 9999) { case _ => log.info("message received") } } +*/ \ No newline at end of file diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index f3ef2987de..b11dc3e682 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -1,43 +1,43 @@ import sbt._ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { - // repos - val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" - val databinder = "DataBinder" at "http://databinder.net/repo" - val configgy = "Configgy" at "http://www.lag.net/repo" - val multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" - val jboss = "jBoss" at "http://repository.jboss.org/maven2" - val guiceyfruit = "GuiceyFruit" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" - val embeddedrepo = "embedded repo" at "http://guice-maven.googlecode.com/svn/trunk" - val google = "google" at "http://google-maven-repository.googlecode.com/svn/repository" - val m2 = "m2" at "http://download.java.net/maven/2" + // repos + val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" + val databinder = "DataBinder" at "http://databinder.net/repo" + val configgy = "Configgy" at "http://www.lag.net/repo" + val multiverse = "Multiverse" at "http://multiverse.googlecode.com/svn/maven-repository/releases" + val jboss = "jBoss" at "http://repository.jboss.org/maven2" + val guiceyfruit = "GuiceyFruit" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" + val embeddedrepo = "embedded repo" at "http://guice-maven.googlecode.com/svn/trunk" + val google = "google" at "http://google-maven-repository.googlecode.com/svn/repository" + val m2 = "m2" at "http://download.java.net/maven/2" - // project versions - val JERSEY_VERSION = "1.1.5" - val ATMO_VERSION = "0.6-SNAPSHOT" - val CASSANDRA_VERSION = "0.5.0" + // project versions + val JERSEY_VERSION = "1.1.5" + val ATMO_VERSION = "0.6-SNAPSHOT" + val CASSANDRA_VERSION = "0.5.0" - // project defintions - lazy val akka_java_util = project("akka-util-java", "akka-java-util", new AkkaJavaUtilProject(_)) - lazy val akka_util = project("akka-util", "akka-util",new AkkaUtilProject(_)) - lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_), akka_util, akka_java_util) - lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) - lazy val akka_rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), akka_core) - lazy val akka_comet = project("akka-comet", "akka-comet",new AkkaCometProject(_), akka_rest) - lazy val akka_patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), akka_core) - lazy val akka_security = project("akka-security", "akka-security", new AkkaSecurityProject(_), akka_core) - lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) - lazy val akka_cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) - lazy val akka_kernel = project("akka-kernel","akka-kernel", new AkkaKernelProject(_),akka_core,akka_rest,akka_persistence,akka_cluster,akka_amqp,akka_security,akka_comet) + // project defintions + lazy val akka_java_util = project("akka-util-java", "akka-java-util", new AkkaJavaUtilProject(_)) + lazy val akka_util = project("akka-util", "akka-util", new AkkaUtilProject(_)) + lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_), akka_util, akka_java_util) + lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) + lazy val akka_rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), akka_core) + lazy val akka_comet = project("akka-comet", "akka-comet", new AkkaCometProject(_), akka_rest) + lazy val akka_patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), akka_core) + lazy val akka_security = project("akka-security", "akka-security", new AkkaSecurityProject(_), akka_core) + lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) + lazy val akka_cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) + lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), akka_core, akka_rest, akka_persistence, akka_cluster, akka_amqp, akka_security, akka_comet) - // examples - lazy val akka_fun_test = project("akka-fun-test-java","akka-fun-test-java", new AkkaFunTestProject(_),akka_kernel) - lazy val akka_samples = project("akka-samples","akka-samples", new AkkaSamplesParentProject(_)) + // examples + lazy val akka_fun_test = project("akka-fun-test-java", "akka-fun-test-java", new AkkaFunTestProject(_), akka_kernel) + lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) // subprojects class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { - val sjson = "sjson.json" % "sjson" % "0.4" % "compile" + val sjson = "sjson.json" % "sjson" % "0.4" % "compile" val werkz = "org.codehaus.aspectwerkz" % "aspectwerkz-nodeps-jdk5" % "2.1" % "compile" val werkz_core = "org.codehaus.aspectwerkz" % "aspectwerkz-jdk5" % "2.1" % "compile" val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" @@ -50,7 +50,7 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val javautils = "org.scala-tools" % "javautils" % "2.7.4-0.1" % "compile" val netty = "org.jboss.netty" % "netty" % "3.2.0.ALPHA3" % "compile" // testing - val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" } @@ -66,11 +66,11 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val multiverse = "org.multiverse" % "multiverse-alpha" % "0.3" % "compile" } - class AkkaAMQPProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaAMQPProject(info: ProjectInfo) extends DefaultProject(info) { val rabbit = "com.rabbitmq" % "amqp-client" % "1.7.2" } - class AkkaRestProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaRestProject(info: ProjectInfo) extends DefaultProject(info) { val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" val jersey = "com.sun.jersey" % "jersey-core" % JERSEY_VERSION % "compile" val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" @@ -79,7 +79,7 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" } - class AkkaCometProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaCometProject(info: ProjectInfo) extends DefaultProject(info) { val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" @@ -87,37 +87,39 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" } - class AkkaPatternsProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaPatternsProject(info: ProjectInfo) extends DefaultProject(info) { // testing - val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" } - class AkkaSecurityProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaSecurityProject(info: ProjectInfo) extends DefaultProject(info) { val annotation = "javax.annotation" % "jsr250-api" % "1.0" val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" // testing - val scalatest= "org.scalatest" % "scalatest" % "1.0" % "test" + val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test" val junit = "junit" % "junit" % "4.5" % "test" val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" } - class AkkaPersistenceCommonProject(info:ProjectInfo) extends DefaultProject(info) { - val thrift = "com.facebook" % "thrift" % "1.0" % "compile" - val commons_pool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" + class AkkaPersistenceCommonProject(info: ProjectInfo) extends DefaultProject(info) { + val thrift = "com.facebook" % "thrift" % "1.0" % "compile" + val commons_pool = "commons-pool" % "commons-pool" % "1.5.1" % "compile" } - - class AkkaRedisProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaRedisProject(info: ProjectInfo) extends DefaultProject(info) { val redis = "com.redis" % "redisclient" % "1.1" % "compile" + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil } - class AkkaMongoProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaMongoProject(info: ProjectInfo) extends DefaultProject(info) { val mongo = "org.mongodb" % "mongo-java-driver" % "1.1" % "compile" + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil } - class AkkaCassandraProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaCassandraProject(info: ProjectInfo) extends DefaultProject(info) { val cassandra = "org.apache.cassandra" % "cassandra" % CASSANDRA_VERSION % "compile" val high_scale = "org.apache.cassandra" % "high-scale-lib" % CASSANDRA_VERSION % "test" val cassandra_clhm = "org.apache.cassandra" % "clhm-production" % CASSANDRA_VERSION % "test" @@ -125,40 +127,43 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val google_coll = "com.google.collections" % "google-collections" % "1.0" % "test" val slf4j = "org.slf4j" % "slf4j-api" % "1.5.8" % "test" val slf4j_log4j = "org.slf4j" % "slf4j-log4j12" % "1.5.8" % "test" - val log4j = "log4j" % "log4j" % "1.2.15" % "test" + val log4j = "log4j" % "log4j" % "1.2.15" % "test" + override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil } - - class AkkaPersistenceParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val akka_persistence_common = project ("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_),akka_core) - lazy val akka_persistence_redis = project("akka-persistence-redis","akka-persistence-redis", new AkkaRedisProject(_),akka_persistence_common) - lazy val akka_persistence_mongo = project("akka-persistence-mongo","akka-persistence-mongo", new AkkaMongoProject(_),akka_persistence_common) - lazy val akka_persistence_cassandra = project("akka-persistence-cassandra","akka-persistence-cassandra", new AkkaCassandraProject(_),akka_persistence_common) + class AkkaPersistenceParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_persistence_common = project("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_), akka_core) + lazy val akka_persistence_redis = project("akka-persistence-redis", "akka-persistence-redis", new AkkaRedisProject(_), akka_persistence_common) + lazy val akka_persistence_mongo = project("akka-persistence-mongo", "akka-persistence-mongo", new AkkaMongoProject(_), akka_persistence_common) + lazy val akka_persistence_cassandra = project("akka-persistence-cassandra", "akka-persistence-cassandra", new AkkaCassandraProject(_), akka_persistence_common) } - - class AkkaJgroupsProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaJgroupsProject(info: ProjectInfo) extends DefaultProject(info) { val jgroups = "jgroups" % "jgroups" % "2.8.0.CR7" % "compile" } - - class AkkaShoalProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaShoalProject(info: ProjectInfo) extends DefaultProject(info) { val shoal = "shoal-jxta" % "shoal" % "1.1-20090818" % "compile" val shoal_extra = "shoal-jxta" % "jxta" % "1.1-20090818" % "compile" } - - class AkkaClusterParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val akka_cluster_jgroups = project("akka-cluster-jgroups","akka-cluster-jgroups", new AkkaJgroupsProject(_),akka_core) - lazy val akka_cluster_shoal = project("akka-cluster-shoal","akka-cluster-shoal", new AkkaShoalProject(_),akka_core) + + class AkkaClusterParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_cluster_jgroups = project("akka-cluster-jgroups", "akka-cluster-jgroups", new AkkaJgroupsProject(_), akka_core) + lazy val akka_cluster_shoal = project("akka-cluster-shoal", "akka-cluster-shoal", new AkkaShoalProject(_), akka_core) } - - class AkkaKernelProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaKernelProject(info: ProjectInfo) extends DefaultProject(info) { val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" val atmo = "org.atmosphere" % "atmosphere-annotations" % ATMO_VERSION % "compile" val atmo_jersey = "org.atmosphere" % "atmosphere-jersey" % ATMO_VERSION % "compile" val atmo_runtime = "org.atmosphere" % "atmosphere-runtime" % ATMO_VERSION % "compile" + + def mainMethod = Some("se.scalablesolutions.akka.Main") + override def packageOptions: Seq[PackageOption] = MainClass("se.scalablesolutions.akka.Main") :: Nil } // examples - class AkkaFunTestProject(info:ProjectInfo) extends DefaultProject(info) { + class AkkaFunTestProject(info: ProjectInfo) extends DefaultProject(info) { val protobuf = "com.google.protobuf" % "protobuf-java" % "2.2.0" val grizzly = "com.sun.grizzly" % "grizzly-comet-webserver" % "1.9.18-i" % "compile" val jersey_server = "com.sun.jersey" % "jersey-server" % JERSEY_VERSION % "compile" @@ -168,35 +173,35 @@ class AkkaParent(info: ProjectInfo) extends ParentProject(info) { val junit = "junit" % "junit" % "4.5" % "test" val jmock = "org.jmock" % "jmock" % "2.4.0" % "test" } - - - class AkkaSampleChatProject(info:ProjectInfo) extends DefaultProject(info) - - class AkkaSampleLiftProject(info:ProjectInfo) extends DefaultProject(info) { + + + class AkkaSampleChatProject(info: ProjectInfo) extends DefaultProject(info) + + class AkkaSampleLiftProject(info: ProjectInfo) extends DefaultProject(info) { val lift = "net.liftweb" % "lift-webkit" % "1.1-M6" % "compile" val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "provided" // testing - val jetty = "org.mortbay.jetty" % "jetty" % "6.1.6" % "test" + val jetty = "org.mortbay.jetty" % "jetty" % "6.1.6" % "test" val junit = "junit" % "junit" % "4.5" % "test" } - - class AkkaSampleRestJavaProject(info:ProjectInfo) extends DefaultProject(info) - - class AkkaSampleRestScalaProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaSampleRestJavaProject(info: ProjectInfo) extends DefaultProject(info) + + class AkkaSampleRestScalaProject(info: ProjectInfo) extends DefaultProject(info) { val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" } - - class AkkaSampleSecurityProject(info:ProjectInfo) extends DefaultProject(info) { + + class AkkaSampleSecurityProject(info: ProjectInfo) extends DefaultProject(info) { val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" } - class AkkaSamplesParentProject(info:ProjectInfo) extends ParentProject(info) { - lazy val akka_sample_chat = project("akka-sample-chat","akka-sample-chat",new AkkaSampleChatProject(_),akka_kernel) - lazy val akka_sample_lift = project("akka-sample-lift","akka-sample-lift",new AkkaSampleLiftProject(_),akka_kernel) - lazy val akka_sample_rest_java = project("akka-sample-rest-java","akka-sample-rest-java",new AkkaSampleRestJavaProject(_),akka_kernel) - lazy val akka_sample_rest_scala = project("akka-sample-rest-scala","akka-sample-rest-scala",new AkkaSampleRestScalaProject(_),akka_kernel) - lazy val akka_sample_security = project("akka-sample-security","akka-sample-security",new AkkaSampleSecurityProject(_),akka_kernel) + class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_sample_chat = project("akka-sample-chat", "akka-sample-chat", new AkkaSampleChatProject(_), akka_kernel) + lazy val akka_sample_lift = project("akka-sample-lift", "akka-sample-lift", new AkkaSampleLiftProject(_), akka_kernel) + lazy val akka_sample_rest_java = project("akka-sample-rest-java", "akka-sample-rest-java", new AkkaSampleRestJavaProject(_), akka_kernel) + lazy val akka_sample_rest_scala = project("akka-sample-rest-scala", "akka-sample-rest-scala", new AkkaSampleRestScalaProject(_), akka_kernel) + lazy val akka_sample_security = project("akka-sample-security", "akka-sample-security", new AkkaSampleSecurityProject(_), akka_kernel) } } From 954c0ad2a14233776d346bf9b32bf89fee9a7092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 5 Mar 2010 16:07:06 +0100 Subject: [PATCH 21/81] Fixed last persistence issues with new STM, all test pass --- .../scala/CassandraPersistentActorSpec.scala | 16 +++++----------- .../test/scala/MongoPersistentActorSpec.scala | 13 ++++++------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala b/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala index 0e232f5ce9..a7fed923eb 100644 --- a/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala @@ -1,13 +1,9 @@ package se.scalablesolutions.akka.state -import se.scalablesolutions.akka.actor.Actor - -import junit.framework.TestCase +import se.scalablesolutions.akka.actor.{Actor, Transactor} import org.junit.Test import org.junit.Assert._ -import org.apache.cassandra.service.CassandraDaemon -import org.junit.BeforeClass import org.junit.Before import org.scalatest.junit.JUnitSuite @@ -28,9 +24,8 @@ case class SetRefStateOneWay(key: String) case class SuccessOneWay(key: String, value: String) case class FailureOneWay(key: String, value: String, failer: Actor) -class CassandraPersistentActor extends Actor { +class CassandraPersistentActor extends Transactor { timeout = 100000 - makeTransactionRequired private lazy val mapState = CassandraStorage.newMap private lazy val vectorState = CassandraStorage.newVector @@ -66,8 +61,7 @@ class CassandraPersistentActor extends Actor { } } -@serializable class PersistentFailerActor extends Actor { - makeTransactionRequired +@serializable class PersistentFailerActor extends Transactor { def receive = { case "Failure" => throw new RuntimeException("expected") @@ -76,8 +70,8 @@ class CassandraPersistentActor extends Actor { class CassandraPersistentActorSpec extends JUnitSuite { - @Before - def startCassandra = EmbeddedCassandraService.start + //@Before + //def startCassandra = EmbeddedCassandraService.start @Test def testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess = { diff --git a/akka-persistence/akka-persistence-mongo/src/test/scala/MongoPersistentActorSpec.scala b/akka-persistence/akka-persistence-mongo/src/test/scala/MongoPersistentActorSpec.scala index 8681ebadb9..8ad5d94355 100644 --- a/akka-persistence/akka-persistence-mongo/src/test/scala/MongoPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-mongo/src/test/scala/MongoPersistentActorSpec.scala @@ -8,7 +8,7 @@ import org.junit.Assert._ import _root_.dispatch.json.{JsNumber, JsValue} import _root_.dispatch.json.Js._ -import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.actor.{Transactor, Actor} /** * A persistent actor based on MongoDB storage. @@ -29,10 +29,10 @@ case class MultiDebit(accountNo: String, amounts: List[BigInt], failer: Actor) case class Credit(accountNo: String, amount: BigInt) case object LogSize -class BankAccountActor extends Actor { - makeTransactionRequired - private val accountState = MongoStorage.newMap - private val txnLog = MongoStorage.newVector +class BankAccountActor extends Transactor { + + private lazy val accountState = MongoStorage.newMap + private lazy val txnLog = MongoStorage.newVector def receive: PartialFunction[Any, Unit] = { // check balance @@ -91,8 +91,7 @@ class BankAccountActor extends Actor { } } -@serializable class PersistentFailerActor extends Actor { - makeTransactionRequired +@serializable class PersistentFailerActor extends Transactor { def receive = { case "Failure" => throw new RuntimeException("expected") From f084e6e076bdc2395380b980552ad953ae5bc874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 5 Mar 2010 17:44:11 +0100 Subject: [PATCH 22/81] removed log.trace that gave bad perf --- akka-core/src/main/scala/actor/Actor.scala | 2 +- akka-core/src/test/scala/PerformanceTest.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index a6b46d903d..60f3967e6c 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -873,7 +873,7 @@ trait Actor extends TransactionManagement { * Callback for the dispatcher. E.g. single entry point to the user code and all protected[this] methods. */ private[akka] def invoke(messageHandle: MessageInvocation) = synchronized { - log.trace("%s is invoked with message %s", toString, messageHandle) + //log.trace("%s is invoked with message %s", toString, messageHandle) try { if (TransactionManagement.isTransactionalityEnabled) transactionalDispatch(messageHandle) else dispatch(messageHandle) diff --git a/akka-core/src/test/scala/PerformanceTest.scala b/akka-core/src/test/scala/PerformanceTest.scala index d58d075202..778e4d45cd 100644 --- a/akka-core/src/test/scala/PerformanceTest.scala +++ b/akka-core/src/test/scala/PerformanceTest.scala @@ -1,4 +1,4 @@ -package test +package se.scalablesolutions.akka import org.scalatest.junit.JUnitSuite import org.junit.Test @@ -279,7 +279,7 @@ class PerformanceTest extends JUnitSuite { var nrOfMessages = 2000000 var nrOfActors = 4 - var akkaTime = stressTestAkkaActors(nrOfMessages, nrOfActors, 1000 * 20) + var akkaTime = stressTestAkkaActors(nrOfMessages, nrOfActors, 1000 * 30) var scalaTime = stressTestScalaActors(nrOfMessages, nrOfActors, 1000 * 40) var ratio: Double = scalaTime.toDouble / akkaTime.toDouble From c0b41379e6ce3cfb07a0cc4db4ba4c4102b7d7eb Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Fri, 5 Mar 2010 19:38:23 +0100 Subject: [PATCH 23/81] Producer trait for producing messages to Camel endpoints (sync/async, oneway/twoway), Immutable representation of Camel message, consumer/producer examples, refactorings/improvements/cleanups. --- .../main/scala/CamelContextLifecycle.scala | 97 ++++++++ akka-camel/src/main/scala/Consumer.scala | 2 +- akka-camel/src/main/scala/Message.scala | 225 ++++++++++++++++-- akka-camel/src/main/scala/Producer.scala | 199 ++++++++++++++++ .../main/scala/component/ActorComponent.scala | 95 +++----- .../scala/service/CamelContextManager.scala | 23 -- .../src/main/scala/service/CamelService.scala | 33 ++- akka-camel/src/test/scala/MessageTest.scala | 65 ++++- akka-camel/src/test/scala/ProducerTest.scala | 108 +++++++++ .../scala/component/ActorComponentTest.scala | 23 +- .../scala/component/ActorProducerTest.scala | 10 +- .../test/scala/service/CamelServiceTest.scala | 52 ++-- .../src/main/scala/Boot.scala | 26 +- .../src/main/scala/Consumer3.scala | 17 ++ .../src/main/scala/Producer1.scala | 17 ++ .../src/main/scala/Transformer.scala | 15 ++ 16 files changed, 822 insertions(+), 185 deletions(-) create mode 100644 akka-camel/src/main/scala/CamelContextLifecycle.scala create mode 100644 akka-camel/src/main/scala/Producer.scala delete mode 100644 akka-camel/src/main/scala/service/CamelContextManager.scala create mode 100644 akka-camel/src/test/scala/ProducerTest.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Producer1.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Transformer.scala diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala new file mode 100644 index 0000000000..68e67a0612 --- /dev/null +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel + +import org.apache.camel.{ProducerTemplate, CamelContext} +import org.apache.camel.impl.DefaultCamelContext + +import se.scalablesolutions.akka.util.Logging + +/** + * Defines the lifecycle of a CamelContext. Allowed state transitions are + * init -> start -> stop -> init -> ... etc. + * + * @author Martin Krasser + */ +trait CamelContextLifecycle extends Logging { + // TODO: enforce correct state transitions + // valid: init -> start -> stop -> init ... + + private var _context: CamelContext = _ + private var _template: ProducerTemplate = _ + + private var _initialized = false + private var _started = false + + /** + * Returns the managed CamelContext. + */ + protected def context: CamelContext = _context + + /** + * Returns the managed ProducerTemplate. + */ + protected def template: ProducerTemplate = _template + + /** + * Sets the managed CamelContext. + */ + protected def context_= (context: CamelContext) { _context = context } + + /** + * Sets the managed ProducerTemplate. + */ + protected def template_= (template: ProducerTemplate) { _template = template } + + def initialized = _initialized + def started = _started + + /** + * Starts the CamelContext and ProducerTemplate. + */ + def start() { + context.start + template.start + _started = true + log.info("Camel context started") + } + + /** + * Stops the CamelContext and ProducerTemplate. + */ + def stop() { + template.stop + context.stop + _initialized = false + _started = false + log.info("Camel context stopped") + } + + /** + * Initializes this lifecycle object with the a DefaultCamelContext. + */ + def init() { + init(new DefaultCamelContext) + } + + /** + * Initializes this lifecycle object with the given CamelContext. + */ + def init(context: CamelContext) { + this.context = context + this.template = context.createProducerTemplate + _initialized = true + log.info("Camel context initialized") + } +} + +/** + * Makes a global CamelContext and ProducerTemplate accessible to applications. The lifecycle + * of these objects is managed by se.scalablesolutions.akka.camel.service.CamelService. + */ +object CamelContextManager extends CamelContextLifecycle { + override def context: CamelContext = super.context + override def template: ProducerTemplate = super.template +} \ No newline at end of file diff --git a/akka-camel/src/main/scala/Consumer.scala b/akka-camel/src/main/scala/Consumer.scala index 3dbb101292..1a3003d863 100644 --- a/akka-camel/src/main/scala/Consumer.scala +++ b/akka-camel/src/main/scala/Consumer.scala @@ -7,7 +7,7 @@ package se.scalablesolutions.akka.camel import se.scalablesolutions.akka.actor.Actor /** - * Mixed in by Actor subclasses to be Camel endpoint consumers. + * Mixed in by Actor implementations that consume message from Camel endpoints. * * @author Martin Krasser */ diff --git a/akka-camel/src/main/scala/Message.scala b/akka-camel/src/main/scala/Message.scala index 88f810e045..db23868fac 100644 --- a/akka-camel/src/main/scala/Message.scala +++ b/akka-camel/src/main/scala/Message.scala @@ -4,58 +4,241 @@ package se.scalablesolutions.akka.camel -import org.apache.camel.{Message => CamelMessage} -import org.apache.camel.impl.DefaultCamelContext +import org.apache.camel.{Exchange, Message => CamelMessage} +import org.apache.camel.util.ExchangeHelper import scala.collection.jcl.{Map => MapWrapper} /** + * An immutable representation of a Camel message. Actor classes that mix in + * se.scalablesolutions.akka.camel.Producer or + * se.scalablesolutions.akka.camel.Consumer use this message type for communication. + * * @author Martin Krasser */ -class Message(val body: Any, val headers: Map[String, Any]) { - +case class Message(val body: Any, val headers: Map[String, Any]) { + /** + * Creates a message with a body and an empty header map. + */ def this(body: Any) = this(body, Map.empty) - def bodyAs[T](clazz: Class[T]): T = Message.converter.mandatoryConvertTo[T](clazz, body) + /** + * Returns the body of the message converted to the type given by the clazz + * argument. Conversion is done using Camel's type converter. The type converter is obtained + * from the CamelContext managed by CamelContextManager. Applications have to ensure proper + * initialization of CamelContextManager. + * + * @see CamelContextManager. + */ + def bodyAs[T](clazz: Class[T]): T = { + CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](clazz, body) + } + /** + * Returns those headers from this message whose name is contained in names. + */ + def headers(names: Set[String]): Map[String, Any] = { + headers.filter(names contains _._1) + } + + /** + * Creates a Message with a new body using a transformer function. + */ + def transformBody[A](transformer: A => Any): Message = setBody(transformer(body.asInstanceOf[A])) + + /** + * Creates a Message with a new body converted to type clazz. + * + * @see Message#bodyAs(Class) + */ + def setBodyAs[T](clazz: Class[T]): Message = setBody(bodyAs(clazz)) + + /** + * Creates a Message with a new body. + */ + def setBody(body: Any) = new Message(body, this.headers) + + /** + * Creates a new Message with new headers. + */ + def setHeaders(headers: Map[String, Any]) = new Message(this.body, headers) + + /** + * Creates a new Message with the headers argument added to the existing headers. + */ + def addHeaders(headers: Map[String, Any]) = new Message(this.body, this.headers ++ headers) + + /** + * Creates a new Message with the header argument added to the existing headers. + */ + def addHeader(header: (String, Any)) = new Message(this.body, this.headers + header) + + /** + * Creates a new Message where the header with name headerName is removed from + * the existing headers. + */ + def removeHeader(headerName: String) = new Message(this.body, this.headers - headerName) } /** + * Companion object of Message class. + * * @author Martin Krasser */ object Message { + /** + * Message header to correlate request with response messages. Applications that send + * messages to a Producer actor may want to set this header on the request message + * so that it can be correlated with an asynchronous response. Messages send to Consumer + * actors have this header already set. + */ + val MessageExchangeId = "MessageExchangeId" - val converter = new DefaultCamelContext().getTypeConverter - + /** + * Creates a new Message with body as message body and an empty header map. + */ def apply(body: Any) = new Message(body) - def apply(body: Any, headers: Map[String, Any]) = new Message(body, headers) + /** + * Creates a canonical form of the given message msg. If msg of type + * Message then msg is returned, otherwise msg is set as body of a + * newly created Message object. + */ + def canonicalize(msg: Any) = msg match { + case mobj: Message => mobj + case body => new Message(body) + } +} - def apply(cm: CamelMessage) = - new Message(cm.getBody, Map.empty ++ MapWrapper[String, AnyRef](cm.getHeaders).elements) +/** + * An immutable representation of a failed Camel exchange. It contains the failure cause + * obtained from Exchange.getException and the headers from either the Exchange.getIn + * message or Exchange.getOut message, depending on the exchange pattern. + * + * @author Martin Krasser + */ +case class Failure(val cause: Throwable, val headers: Map[String, Any]) + +/** + * Adapter for converting an org.apache.camel.Exchange to and from Message and Failure objects. + * + * @author Martin Krasser + */ +class CamelExchangeAdapter(exchange: Exchange) { + + import CamelMessageConversion.toMessageAdapter + + /** + * Sets Exchange.getIn from the given Message object. + */ + def fromRequestMessage(msg: Message): Exchange = { requestMessage.fromMessage(msg); exchange } + + /** + * Depending on the exchange pattern, sets Exchange.getIn or Exchange.getOut from the given + * Message object. If the exchange is out-capable then the Exchange.getOut is set, otherwise + * Exchange.getIn. + */ + def fromResponseMessage(msg: Message): Exchange = { responseMessage.fromMessage(msg); exchange } + + /** + * Creates a Message object from Exchange.getIn. + */ + def toRequestMessage: Message = toRequestMessage(Map.empty) + + /** + * Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut. + * If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn. + */ + def toResponseMessage: Message = toResponseMessage(Map.empty) + + /** + * Creates a Failure object from the adapted Exchange. + * + * @see Failure + */ + def toFailureMessage: Failure = toFailureMessage(Map.empty) + + /** + * Creates a Message object from Exchange.getIn. + * + * @param headers additional headers to set on the created Message in addition to those + * in the Camel message. + */ + def toRequestMessage(headers: Map[String, Any]): Message = requestMessage.toMessage(headers) + + /** + * Depending on the exchange pattern, creates a Message object from Exchange.getIn or Exchange.getOut. + * If the exchange is out-capable then the Exchange.getOut is set, otherwise Exchange.getIn. + * + * @param headers additional headers to set on the created Message in addition to those + * in the Camel message. + */ + def toResponseMessage(headers: Map[String, Any]): Message = responseMessage.toMessage(headers) + + /** + * Creates a Failure object from the adapted Exchange. + * + * @param headers additional headers to set on the created Message in addition to those + * in the Camel message. + * + * @see Failure + */ + def toFailureMessage(headers: Map[String, Any]): Failure = Failure(exchange.getException, headers ++ responseMessage.toMessage.headers) + + private def requestMessage = exchange.getIn + + private def responseMessage = ExchangeHelper.getResultMessage(exchange) } /** - * @author Martin Krasser + * Adapter for converting an org.apache.camel.Message to and from Message objects. + * + * @author Martin Krasser */ -class CamelMessageWrapper(val cm: CamelMessage) { - - def from(m: Message): CamelMessage = { +class CamelMessageAdapter(val cm: CamelMessage) { + /** + * Set the adapted Camel message from the given Message object. + */ + def fromMessage(m: Message): CamelMessage = { cm.setBody(m.body) - for (h <- m.headers) { - cm.getHeaders.put(h._1, h._2.asInstanceOf[AnyRef]) - } + for (h <- m.headers) cm.getHeaders.put(h._1, h._2.asInstanceOf[AnyRef]) cm } + /** + * Creates a new Message object from the adapted Camel message. + */ + def toMessage: Message = toMessage(Map.empty) + + /** + * Creates a new Message object from the adapted Camel message. + * + * @param headers additional headers to set on the created Message in addition to those + * in the Camel message. + */ + def toMessage(headers: Map[String, Any]): Message = { + Message(cm.getBody, cmHeaders(headers, cm)) + } + + private def cmHeaders(headers: Map[String, Any], cm: CamelMessage) = { + headers ++ MapWrapper[String, AnyRef](cm.getHeaders).elements + } + } /** - * @author Martin Krasser + * Defines conversion methods to CamelExchangeAdapter and CamelMessageAdapter. Imported by applications + * that implicitly want to use conversion methods of CamelExchangeAdapter and CamelMessageAdapter. */ -object CamelMessageWrapper { - - implicit def wrapCamelMessage(cm: CamelMessage): CamelMessageWrapper = new CamelMessageWrapper(cm) +object CamelMessageConversion { + /** + * Creates an CamelExchangeAdapter for the given Camel exchange. + */ + implicit def toExchangeAdapter(ce: Exchange): CamelExchangeAdapter = new CamelExchangeAdapter(ce) + /** + * Creates an CamelMessageAdapter for the given Camel message. + */ + implicit def toMessageAdapter(cm: CamelMessage): CamelMessageAdapter = new CamelMessageAdapter(cm) } \ No newline at end of file diff --git a/akka-camel/src/main/scala/Producer.scala b/akka-camel/src/main/scala/Producer.scala new file mode 100644 index 0000000000..326f208cfc --- /dev/null +++ b/akka-camel/src/main/scala/Producer.scala @@ -0,0 +1,199 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.camel + +import CamelMessageConversion.toExchangeAdapter + +import org.apache.camel.{Processor, ExchangePattern, Exchange, ProducerTemplate} +import org.apache.camel.impl.DefaultExchange +import org.apache.camel.spi.Synchronization + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.dispatch.CompletableFutureResult +import se.scalablesolutions.akka.util.Logging + +/** + * Mixed in by Actor implementations that produce messages to Camel endpoints. + * + * @author Martin Krasser + */ +trait Producer { + + self: Actor => + + /** + * If set to true (default), communication with the Camel endpoint is done via the Camel + * Async API. Camel then processes the + * message in a separate thread. If set to false, the actor thread is blocked until Camel + * has finished processing the produced message. + */ + def async: Boolean = true + + /** + * If set to false (default), this producer expects a response message from the Camel endpoint. + * If set to true, this producer communicates with the Camel endpoint with an in-only message + * exchange pattern (fire and forget). + */ + def oneway: Boolean = false + + /** + * Returns the Camel endpoint URI to produce messages to. + */ + def endpointUri: String + + /** + * Returns the names of message headers to copy from a request message to a response message. + * By default only the Message.MessageExchangeId is copied. Applications may override this to + * define an application-specific set of message headers to copy. + */ + def headersToCopy: Set[String] = Set(Message.MessageExchangeId) + + /** + * Returns the producer template from the CamelContextManager. Applications either have to ensure + * proper initialization of CamelContextManager or override this method. + * + * @see CamelContextManager. + */ + protected def template: ProducerTemplate = CamelContextManager.template + + /** + * Initiates a one-way (in-only) message exchange to the Camel endpoint given by + * endpointUri. This method blocks until Camel finishes processing + * the message exchange. + * + * @param msg: the message to produce. The message is converted to its canonical + * representation via Message.canonicalize. + */ + protected def produceOneway(msg: Any): Unit = { + template.send(endpointUri, createInOnlyExchange.fromRequestMessage(Message.canonicalize(msg))) + } + + /** + * Initiates a one-way (in-only) message exchange to the Camel endpoint given by + * endpointUri. This method triggers asynchronous processing of the + * message exchange by Camel. + * + * @param msg: the message to produce. The message is converted to its canonical + * representation via Message.canonicalize. + */ + protected def produceOnewayAsync(msg: Any): Unit = { + template.asyncSend(endpointUri, createInOnlyExchange.fromRequestMessage(Message.canonicalize(msg))) + } + + /** + * Initiates a two-way (in-out) message exchange to the Camel endpoint given by + * endpointUri. This method blocks until Camel finishes processing + * the message exchange. + * + * @param msg: the message to produce. The message is converted to its canonical + * representation via Message.canonicalize. + * @return either a response Message or a Failure object. + */ + protected def produce(msg: Any): Any = { + val cmsg = Message.canonicalize(msg) + val requestProcessor = new Processor() { + def process(exchange: Exchange) = exchange.fromRequestMessage(cmsg) + } + val result = template.request(endpointUri, requestProcessor) + if (result.isFailed) + result.toFailureMessage(cmsg.headers(headersToCopy)) + else + result.toResponseMessage(cmsg.headers(headersToCopy)) + } + + /** + * Initiates a two-way (in-out) message exchange to the Camel endpoint given by + * endpointUri. This method triggers asynchronous processing of the + * message exchange by Camel. The response message is returned asynchronously to + * the original sender (or sender future). + * + * @param msg: the message to produce. The message is converted to its canonical + * representation via Message.canonicalize. + * @return either a response Message or a Failure object. + * @see ProducerResponseSender + */ + protected def produceAsync(msg: Any): Unit = { + val cmsg = Message.canonicalize(msg) + val sync = new ProducerResponseSender(cmsg.headers(headersToCopy), this.sender, this.senderFuture, this) + template.asyncCallback(endpointUri, createInOutExchange.fromRequestMessage(cmsg), sync) + } + + /** + * Default implementation for Actor.receive. Implementors may choose to + * def receive = produce. This partial function calls one of + * the protected produce methods depending on the return values of + * oneway and async. + */ + protected def produce: PartialFunction[Any, Unit] = { + case msg => { + if ( oneway && !async) produceOneway(msg) + else if ( oneway && async) produceOnewayAsync(msg) + else if (!oneway && !async) reply(produce(msg)) + else /*(!oneway && async)*/ produceAsync(msg) + } + } + + /** + * Creates a new in-only Exchange. + */ + protected def createInOnlyExchange: Exchange = createExchange(ExchangePattern.InOnly) + + /** + * Creates a new in-out Exchange. + */ + protected def createInOutExchange: Exchange = createExchange(ExchangePattern.InOut) + + /** + * Creates a new Exchange with given pattern from the CamelContext managed by + * CamelContextManager. Applications either have to ensure proper initialization + * of CamelContextManager or override this method. + * + * @see CamelContextManager. + */ + protected def createExchange(pattern: ExchangePattern): Exchange = { + new DefaultExchange(CamelContextManager.context, pattern) + } +} + +/** + * Synchronization object that sends responses asynchronously to initial senders. This + * class is used by Producer for asynchronous two-way messaging with a Camel endpoint. + * + * @author Martin Krasser + */ +class ProducerResponseSender( + headers: Map[String, Any], + sender: Option[Actor], + senderFuture: Option[CompletableFutureResult], + producer: Actor) extends Synchronization with Logging { + + implicit val producerActor = Some(producer) // the response sender + + /** + * Replies a Failure message, created from the given exchange, to sender (or + * senderFuture if applicable). + */ + def onFailure(exchange: Exchange) = { + reply(exchange.toFailureMessage(headers)) + } + + /** + * Replies a response Message, created from the given exchange, to sender (or + * senderFuture if applicable). + */ + def onComplete(exchange: Exchange) = { + reply(exchange.toResponseMessage(headers)) + } + + private def reply(message: Any) = { + sender match { + case Some(actor) => actor ! message + case None => senderFuture match { + case Some(future) => future.completeWithResult(message) + case None => log.warning("no destination for sending response") + } + } + } +} diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala index d59a4261c9..71db14021a 100644 --- a/akka-camel/src/main/scala/component/ActorComponent.scala +++ b/akka-camel/src/main/scala/component/ActorComponent.scala @@ -11,10 +11,10 @@ import org.apache.camel.{Exchange, Consumer, Processor} import org.apache.camel.impl.{DefaultProducer, DefaultEndpoint, DefaultComponent} import se.scalablesolutions.akka.actor.{ActorRegistry, Actor} -import se.scalablesolutions.akka.camel.{CamelMessageWrapper, Message} +import se.scalablesolutions.akka.camel.{CamelMessageConversion, Message} /** - * Camel component for interacting with actors. + * Camel component for sending messages to and receiving replies from actors. * * @see se.scalablesolutions.akka.camel.component.ActorEndpoint * @see se.scalablesolutions.akka.camel.component.ActorProducer @@ -22,7 +22,6 @@ import se.scalablesolutions.akka.camel.{CamelMessageWrapper, Message} * @author Martin Krasser */ class ActorComponent extends DefaultComponent { - def createEndpoint(uri: String, remaining: String, parameters: JavaMap[String, Object]): ActorEndpoint = { val idAndUuid = idAndUuidPair(remaining) new ActorEndpoint(uri, this, idAndUuid._1, idAndUuid._2) @@ -37,12 +36,12 @@ class ActorComponent extends DefaultComponent { "invalid path format: %s - should be or id: or uuid:" format remaining) } } - } /** - * Camel endpoint for interacting with actors. An actor can be addressed by its - * Actor.getId or its Actor.uuid combination. Supported URI formats are + * Camel endpoint for referencing an actor. The actor reference is given by the endpoint URI. + * An actor can be referenced by its Actor.getId or its Actor.uuid. + * Supported endpoint URI formats are * actor:<actorid>, * actor:id:<actorid> and * actor:uuid:<actoruuid>. @@ -53,25 +52,27 @@ class ActorComponent extends DefaultComponent { * @author Martin Krasser */ class ActorEndpoint(uri: String, comp: ActorComponent, val id: Option[String], val uuid: Option[String]) extends DefaultEndpoint(uri, comp) { - /** * @throws UnsupportedOperationException */ def createConsumer(processor: Processor): Consumer = throw new UnsupportedOperationException("actor consumer not supported yet") + /** + * Creates a new ActorProducer instance initialized with this endpoint. + */ def createProducer: ActorProducer = new ActorProducer(this) + /** + * Returns true. + */ def isSingleton: Boolean = true - } /** * Sends the in-message of an exchange to an actor. If the exchange pattern is out-capable, * the producer waits for a reply (using the !! operator), otherwise the ! operator is used - * for sending the message. Asynchronous communication is not implemented yet but will be - * added for Camel components that support the Camel Async API (like the jetty component that - * makes use of Jetty continuations). + * for sending the message. * * @see se.scalablesolutions.akka.camel.component.ActorComponent * @see se.scalablesolutions.akka.camel.component.ActorEndpoint @@ -79,9 +80,17 @@ class ActorEndpoint(uri: String, comp: ActorComponent, val id: Option[String], v * @author Martin Krasser */ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { + import CamelMessageConversion.toExchangeAdapter - implicit val sender = Some(new Sender) + implicit val sender = None + /** + * Depending on the exchange pattern, this method either calls processInOut or + * processInOnly for interacting with an actor. This methods looks up the actor + * from the ActorRegistry according to this producer's endpoint URI. + * + * @param exchange represents the message exchange with the actor. + */ def process(exchange: Exchange) { val actor = target getOrElse (throw new ActorNotRegisteredException(ep.getEndpointUri)) if (exchange.getPattern.isOutCapable) @@ -90,40 +99,29 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { processInOnly(exchange, actor) } - override def start { - super.start - sender.get.start - } - - override def stop { - sender.get.stop - super.stop - } - - protected def receive = { - throw new UnsupportedOperationException - } - + /** + * Send the exchange in-message to the given actor using the ! operator. The message + * send to the actor is of type se.scalablesolutions.akka.camel.Message. + */ protected def processInOnly(exchange: Exchange, actor: Actor) { - actor ! Message(exchange.getIn) + actor ! exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId)) } + /** + * Send the exchange in-message to the given actor using the !! operator. The exchange + * out-message is populated from the actor's reply message. The message sent to the + * actor is of type se.scalablesolutions.akka.camel.Message. + */ protected def processInOut(exchange: Exchange, actor: Actor) { - import CamelMessageWrapper._ - - // TODO: make timeout configurable // TODO: support asynchronous communication - // - jetty component: jetty continuations - // - file component: completion callbacks - val result: Any = actor !! Message(exchange.getIn) + val result: Any = actor !! exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId)) result match { - case Some(m:Message) => { - exchange.getOut.from(m) - } - case Some(body) => { - exchange.getOut.setBody(body) + case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg)) + case None => { + // TODO: handle timeout properly + // TODO: make timeout configurable } } } @@ -140,33 +138,14 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { } private def targetByUuid(uuid: String) = ActorRegistry.actorFor(uuid) - } /** - * Generic message sender used by ActorProducer. - * - * @author Martin Krasser - */ -private[component] class Sender extends Actor { - - /** - * Ignores any message. - */ - protected def receive = { - case _ => { /* ignore any reply */ } - } - -} - -/** - * Thrown to indicate that an actor referenced by an endpoint URI cannot be + * Thrown to indicate that an actor referenced by an endpoint URI cannot be * found in the ActorRegistry. * * @author Martin Krasser */ class ActorNotRegisteredException(uri: String) extends RuntimeException { - override def getMessage = "%s not registered" format uri - } \ No newline at end of file diff --git a/akka-camel/src/main/scala/service/CamelContextManager.scala b/akka-camel/src/main/scala/service/CamelContextManager.scala deleted file mode 100644 index a6f84c158c..0000000000 --- a/akka-camel/src/main/scala/service/CamelContextManager.scala +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.camel.service - -import org.apache.camel.CamelContext -import org.apache.camel.impl.DefaultCamelContext - -/** - * Manages the CamelContext used by CamelService. - * - * @author Martin Krasser - */ -object CamelContextManager { - - /** - * The CamelContext used by CamelService. Can be modified by applications prior to - * loading the CamelService. - */ - var context: CamelContext = new DefaultCamelContext - -} \ No newline at end of file diff --git a/akka-camel/src/main/scala/service/CamelService.scala b/akka-camel/src/main/scala/service/CamelService.scala index 98a767797b..aa4a54ef12 100644 --- a/akka-camel/src/main/scala/service/CamelService.scala +++ b/akka-camel/src/main/scala/service/CamelService.scala @@ -10,11 +10,15 @@ import org.apache.camel.builder.RouteBuilder import se.scalablesolutions.akka.actor.{Actor, ActorRegistry} import se.scalablesolutions.akka.annotation.consume -import se.scalablesolutions.akka.camel.Consumer import se.scalablesolutions.akka.util.{Bootable, Logging} +import se.scalablesolutions.akka.camel.{CamelContextManager, Consumer} /** - * Started by the Kernel to expose actors as Camel endpoints. + * Started by the Kernel to expose certain actors as Camel endpoints. It uses + * se.scalablesolutions.akka.camel.CamelContextManage to create and manage the + * lifecycle of a global CamelContext. This class further uses the + * se.scalablesolutions.akka.camel.service.CamelServiceRouteBuilder to implement + * routes from Camel endpoints to actors. * * @see CamelRouteBuilder * @@ -22,42 +26,35 @@ import se.scalablesolutions.akka.util.{Bootable, Logging} */ trait CamelService extends Bootable with Logging { - import CamelContextManager.context + import CamelContextManager._ abstract override def onLoad = { super.onLoad - context.addRoutes(new CamelRouteBuilder) + if (!initialized) init() + context.addRoutes(new CamelServiceRouteBuilder) context.setStreamCaching(true) - context.start - log.info("Camel context started") + start() } abstract override def onUnload = { + stop() super.onUnload - context.stop - log.info("Camel context stopped") } } /** - * Generic route builder that searches the registry for actors that are - * either annotated with @se.scalablesolutions.akka.annotation.consume or - * mixed in se.scalablesolutions.akka.camel.Consumer and exposes them - * as Camel endpoints. + * Implements routes from Camel endpoints to actors. It searches the registry for actors + * that are either annotated with @se.scalablesolutions.akka.annotation.consume or mix in + * se.scalablesolutions.akka.camel.Consumer and exposes them as Camel endpoints. * * @author Martin Krasser */ -class CamelRouteBuilder extends RouteBuilder with Logging { +class CamelServiceRouteBuilder extends RouteBuilder with Logging { def configure = { val actors = ActorRegistry.actors - // - // TODO: resolve/clarify issues with ActorRegistry - // - multiple registration with same id/uuid possible - // - // TODO: avoid redundant registrations actors.filter(isConsumeAnnotated _).foreach { actor: Actor => val fromUri = actor.getClass.getAnnotation(classOf[consume]).value() diff --git a/akka-camel/src/test/scala/MessageTest.scala b/akka-camel/src/test/scala/MessageTest.scala index 791f243ee4..850119b5aa 100644 --- a/akka-camel/src/test/scala/MessageTest.scala +++ b/akka-camel/src/test/scala/MessageTest.scala @@ -1,24 +1,79 @@ -package se.scalablesolutions.akka.camel.service +package se.scalablesolutions.akka.camel import java.io.InputStream import org.apache.camel.NoTypeConversionAvailableException import org.junit.Assert._ -import org.junit.Test import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.camel.Message +import org.junit.Test class MessageTest extends JUnitSuite { + // + // TODO: extend/rewrite unit tests + // These tests currently only ensure proper functioning of basic features. + // + @Test def shouldConvertDoubleBodyToString = { - assertEquals("1.4", new Message(1.4, null).bodyAs(classOf[String])) + CamelContextManager.init() + assertEquals("1.4", Message(1.4, null).bodyAs(classOf[String])) } @Test def shouldThrowExceptionWhenConvertingDoubleBodyToInputStream { + CamelContextManager.init() intercept[NoTypeConversionAvailableException] { - new Message(1.4, null).bodyAs(classOf[InputStream]) + Message(1.4, null).bodyAs(classOf[InputStream]) } } + @Test def shouldReturnSubsetOfHeaders = { + val message = Message("test" , Map("A" -> "1", "B" -> "2")) + assertEquals(Map("B" -> "2"), message.headers(Set("B"))) + } + + @Test def shouldTransformBodyAndPreserveHeaders = { + assertEquals( + Message("ab", Map("A" -> "1")), + Message("a" , Map("A" -> "1")).transformBody[String](body => body + "b")) + } + + @Test def shouldConvertBodyAndPreserveHeaders = { + CamelContextManager.init() + assertEquals( + Message("1.4", Map("A" -> "1")), + Message(1.4 , Map("A" -> "1")).setBodyAs(classOf[String])) + } + + @Test def shouldSetBodyAndPreserveHeaders = { + assertEquals( + Message("test2" , Map("A" -> "1")), + Message("test1" , Map("A" -> "1")).setBody("test2")) + } + + @Test def shouldSetHeadersAndPreserveBody = { + assertEquals( + Message("test1" , Map("C" -> "3")), + Message("test1" , Map("A" -> "1")).setHeaders(Map("C" -> "3"))) + + } + + @Test def shouldAddHeaderAndPreserveBodyAndHeaders = { + assertEquals( + Message("test1" , Map("A" -> "1", "B" -> "2")), + Message("test1" , Map("A" -> "1")).addHeader("B" -> "2")) + } + + @Test def shouldAddHeadersAndPreserveBodyAndHeaders = { + assertEquals( + Message("test1" , Map("A" -> "1", "B" -> "2")), + Message("test1" , Map("A" -> "1")).addHeaders(Map("B" -> "2"))) + } + + @Test def shouldRemoveHeadersAndPreserveBodyAndRemainingHeaders = { + assertEquals( + Message("test1" , Map("A" -> "1")), + Message("test1" , Map("A" -> "1", "B" -> "2")).removeHeader("B")) + } + } \ No newline at end of file diff --git a/akka-camel/src/test/scala/ProducerTest.scala b/akka-camel/src/test/scala/ProducerTest.scala new file mode 100644 index 0000000000..1a69316836 --- /dev/null +++ b/akka-camel/src/test/scala/ProducerTest.scala @@ -0,0 +1,108 @@ +package se.scalablesolutions.akka.camel + +import org.apache.camel.{Exchange, Processor} +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.component.mock.MockEndpoint +import org.junit.Assert._ +import org.junit.{Test, After, Before} +import org.scalatest.junit.JUnitSuite + +import se.scalablesolutions.akka.actor.Actor + +class ProducerTest extends JUnitSuite { + + // + // TODO: extend/rewrite unit tests + // These tests currently only ensure proper functioning of basic features. + // + + import CamelContextManager._ + + var mock: MockEndpoint = _ + + @Before def setUp = { + init() + context.addRoutes(new TestRouteBuilder) + start() + mock = context.getEndpoint("mock:mock", classOf[MockEndpoint]) + } + + @After def tearDown = { + stop() + } + + // + // TODO: test replies to messages sent with ! (bang) + // + + @Test def shouldProduceMessageSyncAndReceiveResponse = { + val producer = new TestProducer("direct:input2", false, false).start + val message = Message("test1", Map(Message.MessageExchangeId -> "123")) + val expected = Message("Hello test1", Map(Message.MessageExchangeId -> "123")) + assertEquals(expected, producer !! message get) + producer.stop + } + + @Test def shouldProduceMessageSyncAndReceiveFailure = { + val producer = new TestProducer("direct:input2", false, false).start + val message = Message("fail", Map(Message.MessageExchangeId -> "123")) + val result = producer.!![Failure](message).get + assertEquals("failure", result.cause.getMessage) + assertEquals(Map(Message.MessageExchangeId -> "123"), result.headers) + producer.stop + } + + @Test def shouldProduceMessageAsyncAndReceiveResponse = { + val producer = new TestProducer("direct:input2", true, false).start + val message = Message("test2", Map(Message.MessageExchangeId -> "124")) + val expected = Message("Hello test2", Map(Message.MessageExchangeId -> "124")) + assertEquals(expected, producer !! message get) + producer.stop + } + + @Test def shouldProduceMessageAsyncAndReceiveFailure = { + val producer = new TestProducer("direct:input2", true, false).start + val message = Message("fail", Map(Message.MessageExchangeId -> "124")) + val result = producer.!![Failure](message).get + assertEquals("failure", result.cause.getMessage) + assertEquals(Map(Message.MessageExchangeId -> "124"), result.headers) + producer.stop + } + + @Test def shouldProduceMessageSyncWithoutReceivingResponse = { + val producer = new TestProducer("direct:input1", false, true).start + mock.expectedBodiesReceived("test3") + producer.!("test3")(None) + producer.stop + } + + @Test def shouldProduceMessageAsyncAndReceiveResponseSync = { + val producer = new TestProducer("direct:input1", true, true).start + mock.expectedBodiesReceived("test4") + producer.!("test4")(None) + producer.stop + } + + class TestProducer(uri:String, prodAsync: Boolean, prodOneway: Boolean) extends Actor with Producer { + override def async = prodAsync + override def oneway = prodOneway + def endpointUri = uri + def receive = produce + } + + class TestRouteBuilder extends RouteBuilder { + def configure { + from("direct:input1").to("mock:mock") + from("direct:input2").process(new Processor() { + def process(exchange: Exchange) = { + val body = exchange.getIn.getBody + body match { + case "fail" => throw new Exception("failure") + case body => exchange.getOut.setBody("Hello %s" format body) + } + } + }) + } + } + +} \ No newline at end of file diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala index 30ea7d1a5b..379349da7a 100644 --- a/akka-camel/src/test/scala/component/ActorComponentTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala @@ -3,27 +3,24 @@ package se.scalablesolutions.akka.camel.component import org.junit._ import org.junit.Assert._ import org.scalatest.junit.JUnitSuite + import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.Message -import org.apache.camel.{CamelContext, ExchangePattern} -import org.apache.camel.impl.{DefaultExchange, SimpleRegistry, DefaultCamelContext} +import se.scalablesolutions.akka.camel.{CamelContextLifecycle, Message} -/** - * @author Martin Krasser - */ -class ActorComponentTest extends JUnitSuite { +class ActorComponentTest extends JUnitSuite with CamelContextLifecycle { - val context = new DefaultCamelContext(new SimpleRegistry) - val template = context.createProducerTemplate + // + // TODO: extend/rewrite unit tests + // These tests currently only ensure proper functioning of basic features. + // @Before def setUp = { - context.start - template.start + init() + start() } @After def tearDown = { - template.stop - context.stop + stop() } @Test def shouldReceiveResponseFromActorReferencedById = { diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala index 73e51ebb04..954a4d21cd 100644 --- a/akka-camel/src/test/scala/component/ActorProducerTest.scala +++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala @@ -1,19 +1,21 @@ package se.scalablesolutions.akka.camel.component import org.apache.camel.{CamelContext, ExchangePattern} +import org.apache.camel.impl.{DefaultCamelContext, DefaultExchange} import org.junit.Assert._ import org.junit.Test import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.camel.Message -import org.apache.camel.impl.{DefaultCamelContext, DefaultExchange} -/** - * @author Martin Krasser - */ class ActorProducerTest extends JUnitSuite { + // + // TODO: extend/rewrite unit tests + // These tests currently only ensure proper functioning of basic features. + // + val context = new DefaultCamelContext val endpoint = context.getEndpoint("actor:%s" format classOf[TestActor].getName) val producer = endpoint.createProducer diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala index 52f6d1fd04..58ccc76273 100644 --- a/akka-camel/src/test/scala/service/CamelServiceTest.scala +++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala @@ -1,68 +1,50 @@ package se.scalablesolutions.akka.camel.service import org.apache.camel.builder.RouteBuilder -import org.apache.camel.impl.DefaultCamelContext import org.junit.Assert._ import org.junit.{Before, After, Test} import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.annotation.consume -import se.scalablesolutions.akka.camel.{Message, Consumer} +import se.scalablesolutions.akka.camel.{CamelContextManager, Consumer, Message} -/** - * @author Martin Krasser - */ -class CamelServiceTest extends JUnitSuite { +class CamelServiceTest extends JUnitSuite with CamelService { - import CamelContextManager.context + // + // TODO: extend/rewrite unit tests + // These tests currently only ensure proper functioning of basic features. + // - context = new DefaultCamelContext - context.addRoutes(new TestBuilder) + import CamelContextManager._ - val template = context.createProducerTemplate - var service: CamelService = _ var actor1: Actor = _ var actor2: Actor = _ var actor3: Actor = _ @Before def setUp = { - service = new CamelService { - override def onUnload = super.onUnload - override def onLoad = super.onLoad - } - actor1 = new TestActor1().start actor2 = new TestActor2().start actor3 = new TestActor3().start - - service.onLoad - template.start - + init() + context.addRoutes(new TestRouteBuilder) + onLoad } @After def tearDown = { + onUnload actor1.stop actor2.stop actor3.stop - - template.stop - service.onUnload } - @Test def shouldReceiveResponseFromActor1ViaGeneratedRoute = { - val result = template.requestBody("direct:actor1", "Martin") - assertEquals("Hello Martin (actor1)", result) + @Test def shouldReceiveResponseViaGeneratedRoute = { + assertEquals("Hello Martin (actor1)", template.requestBody("direct:actor1", "Martin")) + assertEquals("Hello Martin (actor2)", template.requestBody("direct:actor2", "Martin")) } - @Test def shouldReceiveResponseFromActor2ViaGeneratedRoute = { - val result = template.requestBody("direct:actor2", "Martin") - assertEquals("Hello Martin (actor2)", result) - } - - @Test def shouldReceiveResponseFromActor3ViaCustomRoute = { - val result = template.requestBody("direct:actor3", "Martin") - assertEquals("Hello Tester (actor3)", result) + @Test def shouldReceiveResponseViaCustomRoute = { + assertEquals("Hello Tester (actor3)", template.requestBody("direct:actor3", "Martin")) } } @@ -91,7 +73,7 @@ class TestActor3 extends Actor { } } -class TestBuilder extends RouteBuilder { +class TestRouteBuilder extends RouteBuilder { def configure { val actorUri = "actor:%s" format classOf[TestActor3].getName from("direct:actor3").transform(constant("Tester")).to("actor:actor3") diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index 0b3726c08b..b23c99dafa 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -1,10 +1,10 @@ package sample.camel import org.apache.camel.builder.RouteBuilder -import org.apache.camel.impl.DefaultCamelContext +import org.apache.camel.{Exchange, Processor} import se.scalablesolutions.akka.actor.SupervisorFactory -import se.scalablesolutions.akka.camel.service.CamelContextManager +import se.scalablesolutions.akka.camel.CamelContextManager import se.scalablesolutions.akka.config.ScalaConfig._ /** @@ -12,10 +12,8 @@ import se.scalablesolutions.akka.config.ScalaConfig._ */ class Boot { - import CamelContextManager.context - - context = new DefaultCamelContext - context.addRoutes(new CustomRouteBuilder) + CamelContextManager.init() + CamelContextManager.context.addRoutes(new CustomRouteBuilder) val factory = SupervisorFactory( SupervisorConfig( @@ -24,13 +22,27 @@ class Boot { Supervise(new Consumer2, LifeCycle(Permanent)) :: Nil)) factory.newInstance.start + val producer = new Producer1 + val mediator = new Transformer(producer) + val consumer = new Consumer3(mediator) + + producer.start + mediator.start + consumer.start + } class CustomRouteBuilder extends RouteBuilder { def configure { val actorUri = "actor:%s" format classOf[Consumer2].getName - from ("jetty:http://0.0.0.0:8877/camel/test2").to(actorUri) + from("jetty:http://0.0.0.0:8877/camel/test2").to(actorUri) + from("direct:welcome").process(new Processor() { + def process(exchange: Exchange) { + exchange.getOut.setBody("Welcome %s" format exchange.getIn.getBody) + } + }) + } } \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala new file mode 100644 index 0000000000..39cf1f0652 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala @@ -0,0 +1,17 @@ +package sample.camel + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.camel.{Message, Consumer} + +/** + * @author Martin Krasser + */ +class Consumer3(transformer: Actor) extends Actor with Consumer { + + def endpointUri = "jetty:http://0.0.0.0:8877/camel/welcome" + + def receive = { + case msg: Message => transformer.forward(msg.setBodyAs(classOf[String])) + } + +} diff --git a/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala new file mode 100644 index 0000000000..11151a58df --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala @@ -0,0 +1,17 @@ +package sample.camel + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.camel.Producer + +/** + * @author Martin Krasser + */ +class Producer1 extends Actor with Producer { + + def endpointUri = "direct:welcome" + + override def oneway = false // default + override def async = true // default + + protected def receive = produce +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala b/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala new file mode 100644 index 0000000000..0df05c594c --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala @@ -0,0 +1,15 @@ +package sample.camel + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.camel.Message + +/** + * @author Martin Krasser + */ +class Transformer(producer: Actor) extends Actor { + + protected def receive = { + case msg: Message => producer.forward(msg.transformBody[String]("- %s -" format _)) + } + +} \ No newline at end of file From 722d3f24fee7ce5a1d94477753ec24b92d4cc220 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Fri, 5 Mar 2010 21:38:23 +0100 Subject: [PATCH 24/81] do not include *QSpec.java for testing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 480ad8723a..49023e42c9 100644 --- a/pom.xml +++ b/pom.xml @@ -306,7 +306,7 @@ **/*Test.java - **/*Spec.java + - - - - - - Akka Release Notes - Jonas Bonér - - - - Clustered Comet using Akka remote actors and clustered membership API - Cluster membership API and implementation based on JGroups - Security module for HTTP-based authentication and authorization - Support for using Scala XML tags in RESTful Actors (scala-jersey) - Support for Comet Actors using Atmosphere - MongoDB as Akka storage backend - Redis as Akka storage backend - Transparent JSON serialization of Scala objects based on SJSON - Kerberos/SPNEGO support for Security module - Implicit sender for remote actors: Remote actors are able to use reply to answer a request - Support for using the Lift Web framework with Actors - Rewritten STM, now integrated with Multiverse STM - Added STM API for atomic {..} and run {..} orElse {..} - Added STM retry - Complete rewrite of the persistence transaction management, now based on Unit of Work and Multiverse STM - Monadic API to TransactionalRef (use it in for-comprehension) - Lightweight actor syntax using one of the Actor.actor(..) methods. F.e: 'val a = actor { case _ => .. }' - Rewritten event-based dispatcher which improved perfomance by 10x, now substantially faster than event-driven Scala Actors - New Scala JSON parser based on sjson - Added zlib compression to remote actors - Added implicit sender reference for fire-forget ('!') message sends - Monadic API to TransactionalRef (use it in for-comprehension) - Smoother web app integration; just add akka.conf to the classpath (WEB-INF/classes), no need for AKKA_HOME or -Dakka.conf=.. - Modularization of distribution into a thin core (actors, remoting and STM) and the rest in submodules - Added 'forward' to Actor, forwards message but keeps original sender address - JSON serialization for Java objects (using Jackson) - JSON serialization for Scala objects (using SJSON) - Added implementation for remote actor reconnect upon failure - Protobuf serialization for Java and Scala objects - SBinary serialization for Scala objects - Protobuf as remote protocol - AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1 - Updated Cassandra integration and CassandraSession API to v0.4 - Added CassandraSession API (with socket pooling) wrapping Cassandra's Thrift API in Scala and Java APIs - CassandraStorage is now works with external Cassandra cluster - ActorRegistry for retrieving Actor instances by class name and by id - SchedulerActor for scheduling periodic tasks - Now start up kernel with 'java -jar dist/akka-0.6.jar' - Added mailing list: akka-user@googlegroups.com - Improved and restructured documentation - New URL: http://akkasource.org - New and much improved docs - Enhanced trapping of failures: 'trapExit = List(classOf[..], classOf[..])' - Upgraded to Netty 3.2, Protobuf 2.2, ScalaTest 1.0, Jersey 1.1.3, Atmosphere 0.4.1, Cassandra 0.4.1, Configgy 1.4 - Lowered actor memory footprint; now an actor consumes ~600 bytes, which mean that you can create 6.5 million on 4 G RAM - Removed concurrent mode - Remote actors are now defined by their UUID (not class name) - Fixed dispatcher bugs - Cleaned up Maven scripts and distribution in general - Fixed many many bugs and minor issues - Fixed inconsistencies and uglyness in Actors API - Removed embedded Cassandra mode - Removed the !? method in Actor (synchronous message send, since it's evil. Use !! with time-out instead. - Removed startup scripts and lib dir - Removed the 'Transient' life-cycle scope since to close to 'Temporary' in semantics. - Removed 'Transient' Actors and restart timeout - - - - \ No newline at end of file From e056af15fa5d989f366c8f5966dd5a979ee20ead Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 11 Mar 2010 15:53:17 +0100 Subject: [PATCH 52/81] support for remote actors, consumer actor publishing at any time --- .../main/scala/component/ActorComponent.scala | 4 +- .../src/main/scala/service/CamelService.scala | 113 ++++++-------- .../scala/service/ConsumerPublisher.scala | 145 ++++++++++++++++++ akka-camel/src/test/scala/ProducerTest.scala | 1 + .../test/scala/service/CamelServiceTest.scala | 33 +++- .../src/main/scala/actor/ActorRegistry.scala | 37 ++++- .../src/main/scala/Actors.scala | 57 +++++++ .../src/main/scala/Application1.scala | 26 ++++ .../src/main/scala/Application2.scala | 22 +++ .../src/main/scala/Boot.scala | 3 - .../src/main/scala/Consumer1.scala | 18 --- .../src/main/scala/Consumer2.scala | 17 -- .../src/main/scala/Consumer3.scala | 17 -- .../src/main/scala/Transformer.scala | 15 -- 14 files changed, 356 insertions(+), 152 deletions(-) create mode 100644 akka-camel/src/main/scala/service/ConsumerPublisher.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Actors.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Application1.scala create mode 100644 akka-samples/akka-sample-camel/src/main/scala/Application2.scala delete mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala delete mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala delete mode 100644 akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala delete mode 100644 akka-samples/akka-sample-camel/src/main/scala/Transformer.scala diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala index 2fa116926c..5788fd9028 100644 --- a/akka-camel/src/main/scala/component/ActorComponent.scala +++ b/akka-camel/src/main/scala/component/ActorComponent.scala @@ -122,8 +122,8 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { case Some(msg: Failure) => exchange.fromFailureMessage(msg) case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg)) case None => { - throw new TimeoutException("communication with %s timed out after %d ms" - format (ep.getEndpointUri, actor.timeout)) + throw new TimeoutException("timeout (%d ms) while waiting response from %s" + format (actor.timeout, ep.getEndpointUri)) } } } diff --git a/akka-camel/src/main/scala/service/CamelService.scala b/akka-camel/src/main/scala/service/CamelService.scala index 0b77e56796..0f61f2c0f3 100644 --- a/akka-camel/src/main/scala/service/CamelService.scala +++ b/akka-camel/src/main/scala/service/CamelService.scala @@ -4,23 +4,14 @@ package se.scalablesolutions.akka.camel.service -import java.io.InputStream - -import org.apache.camel.builder.RouteBuilder - -import se.scalablesolutions.akka.actor.{Actor, ActorRegistry} -import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.actor.ActorRegistry +import se.scalablesolutions.akka.camel.CamelContextManager import se.scalablesolutions.akka.util.{Bootable, Logging} -import se.scalablesolutions.akka.camel.{CamelContextManager, Consumer} /** - * Started by the Kernel to expose certain actors as Camel endpoints. It uses - * se.scalablesolutions.akka.camel.CamelContextManage to create and manage the - * lifecycle of a global CamelContext. This class further uses the - * se.scalablesolutions.akka.camel.service.CamelServiceRouteBuilder to implement - * routes from Camel endpoints to actors. - * - * @see CamelRouteBuilder + * Used by applications (and the Kernel) to publish consumer actors via Camel + * endpoints and to manage the life cycle of a a global CamelContext which can + * be accessed via se.scalablesolutions.akka.camel.CamelContextManager. * * @author Martin Krasser */ @@ -28,28 +19,63 @@ trait CamelService extends Bootable with Logging { import CamelContextManager._ + private[camel] val consumerPublisher = new ConsumerPublisher + private[camel] val publishRequestor = new PublishRequestor(consumerPublisher) + + /** + * Starts the CamelService. Any started actor that is a consumer actor will be (asynchronously) + * published as Camel endpoint. Consumer actors that are started after this method returned will + * be published as well. Actor publishing is done asynchronously. + */ abstract override def onLoad = { super.onLoad + + // Only init and start if not already done by application if (!initialized) init() - context.addRoutes(new CamelServiceRouteBuilder) + if (!started) start() + + // Camel should cache input streams context.setStreamCaching(true) - start() + + // start actor that exposes consumer actors via Camel endpoints + consumerPublisher.start + + // add listener for actor registration events + ActorRegistry.addRegistrationListener(publishRequestor.start) + + // publish already registered consumer actors + for (publish <- Publish.forConsumers(ActorRegistry.actors)) consumerPublisher.!(publish)(None) } + /** + * Stops the CamelService. + */ abstract override def onUnload = { + ActorRegistry.removeRegistrationListener(publishRequestor) + publishRequestor.stop + consumerPublisher.stop stop() super.onUnload } + /** + * Starts the CamelService. + * + * @see onLoad + */ def load = onLoad + /** + * Stops the CamelService. + * + * @see onUnload + */ def unload = onUnload - } /** * CamelService companion object used by standalone applications to create their own - * CamelService instances. + * CamelService instance. * * @author Martin Krasser */ @@ -59,55 +85,4 @@ object CamelService { * Creates a new CamelService instance. */ def newInstance: CamelService = new CamelService {} - -} - -/** - * Implements routes from Camel endpoints to actors. It searches the registry for actors - * that are either annotated with @se.scalablesolutions.akka.annotation.consume or mix in - * se.scalablesolutions.akka.camel.Consumer and exposes them as Camel endpoints. - * - * @author Martin Krasser - */ -class CamelServiceRouteBuilder extends RouteBuilder with Logging { - - def configure = { - val actors = ActorRegistry.actors - - // TODO: avoid redundant registrations - actors.filter(isConsumeAnnotated _).foreach { actor: Actor => - val fromUri = actor.getClass.getAnnotation(classOf[consume]).value() - configure(fromUri, "actor:id:%s" format actor.getId) - log.debug("registered actor (id=%s) for consuming messages from %s " - format (actor.getId, fromUri)) - } - - // TODO: avoid redundant registrations - actors.filter(isConsumerInstance _).foreach { actor: Actor => - val fromUri = actor.asInstanceOf[Consumer].endpointUri - configure(fromUri, "actor:uuid:%s" format actor.uuid) - log.debug("registered actor (uuid=%s) for consuming messages from %s " - format (actor.uuid, fromUri)) - } - } - - private def configure(fromUri: String, toUri: String) { - val schema = fromUri take fromUri.indexOf(":") // e.g. "http" from "http://whatever/..." - bodyConversions.get(schema) match { - case Some(clazz) => from(fromUri).convertBodyTo(clazz).to(toUri) - case None => from(fromUri).to(toUri) - } - } - - // TODO: make conversions configurable - private def bodyConversions = Map( - "file" -> classOf[InputStream] - ) - - private def isConsumeAnnotated(actor: Actor) = - actor.getClass.getAnnotation(classOf[consume]) ne null - - private def isConsumerInstance(actor: Actor) = - actor.isInstanceOf[Consumer] - } diff --git a/akka-camel/src/main/scala/service/ConsumerPublisher.scala b/akka-camel/src/main/scala/service/ConsumerPublisher.scala new file mode 100644 index 0000000000..4e5adea0b8 --- /dev/null +++ b/akka-camel/src/main/scala/service/ConsumerPublisher.scala @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ +package se.scalablesolutions.akka.camel.service + +import java.io.InputStream +import java.util.concurrent.CountDownLatch + +import org.apache.camel.builder.RouteBuilder + +import se.scalablesolutions.akka.actor.{ActorUnregistered, ActorRegistered, Actor} +import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.camel.{Consumer, CamelContextManager} +import se.scalablesolutions.akka.util.Logging + +/** + * Actor that publishes consumer actors as Camel endpoints at the CamelContext managed + * by se.scalablesolutions.akka.camel.CamelContextManager. It accepts messages of type + * se.scalablesolutions.akka.camel.service.Publish. + * + * @author Martin Krasser + */ +class ConsumerPublisher extends Actor with Logging { + @volatile private var latch = new CountDownLatch(0) + + /** + * Adds a route to the actor identified by a Publish message to the global CamelContext. + */ + protected def receive = { + case p: Publish => publish(new ConsumerRoute(p.endpointUri, p.id, p.uuid)) + case _ => { /* ignore */} + } + + /** + * Sets the number of expected Publish messages received by this actor. Used for testing + * only. + */ + private[camel] def expectPublishCount(count: Int) { + latch = new CountDownLatch(count) + } + + /** + * Waits for the number of expected Publish messages to arrive. Used for testing only. + */ + private[camel] def awaitPublish = latch.await + + private def publish(route: ConsumerRoute) { + CamelContextManager.context.addRoutes(route) + log.info("published actor via endpoint %s" format route.endpointUri) + latch.countDown // needed for testing only. + } +} + +/** + * Defines the route to a consumer actor. + * + * @param endpointUri endpoint URI of the consumer actor + * @param id actor identifier + * @param uuid true if id refers to Actor.uuid, false if + * id refers to Acotr.getId. + * + * @author Martin Krasser + */ +class ConsumerRoute(val endpointUri: String, id: String, uuid: Boolean) extends RouteBuilder { + // TODO: make conversions configurable + private val bodyConversions = Map( + "file" -> classOf[InputStream] + ) + + def configure = { + val schema = endpointUri take endpointUri.indexOf(":") // e.g. "http" from "http://whatever/..." + bodyConversions.get(schema) match { + case Some(clazz) => from(endpointUri).convertBodyTo(clazz).to(actorUri) + case None => from(endpointUri).to(actorUri) + } + } + + private def actorUri = (if (uuid) "actor:uuid:%s" else "actor:id:%s") format id +} + +/** + * A registration listener that publishes consumer actors (and ignores other actors). + * + * @author Martin Krasser + */ +class PublishRequestor(consumerPublisher: Actor) extends Actor { + protected def receive = { + case ActorUnregistered(actor) => { /* ignore */ } + case ActorRegistered(actor) => Publish.forConsumer(actor) match { + case Some(publish) => consumerPublisher ! publish + case None => { /* ignore */ } + } + } +} + +/** + * Request message for publishing a consumer actor. + * + * @param endpointUri endpoint URI of the consumer actor + * @param id actor identifier + * @param uuid true if id refers to Actor.uuid, false if + * id refers to Acotr.getId. + * + * @author Martin Krasser + */ +case class Publish(endpointUri: String, id: String, uuid: Boolean) + +/** + * @author Martin Krasser + */ +object Publish { + /** + * Creates a list of Publish request messages for all consumer actors in the actors + * list. + */ + def forConsumers(actors: List[Actor]): List[Publish] = { + for (actor <- actors; pub = forConsumer(actor); if pub.isDefined) yield pub.get + } + + /** + * Creates a Publish request message if actor is a consumer actor. + */ + def forConsumer(actor: Actor): Option[Publish] = { + forConsumeAnnotated(actor) orElse forConsumerType(actor) + } + + private def forConsumeAnnotated(actor: Actor): Option[Publish] = { + val annotation = actor.getClass.getAnnotation(classOf[consume]) + if (annotation eq null) + None + else if (actor._remoteAddress.isDefined) + None // do not publish proxies + else + Some(Publish(annotation.value, actor.getId, false)) + } + + private def forConsumerType(actor: Actor): Option[Publish] = { + if (!actor.isInstanceOf[Consumer]) + None + else if (actor._remoteAddress.isDefined) + None + else + Some(Publish(actor.asInstanceOf[Consumer].endpointUri, actor.uuid, true)) + } +} diff --git a/akka-camel/src/test/scala/ProducerTest.scala b/akka-camel/src/test/scala/ProducerTest.scala index 1a69316836..268c8c6a6a 100644 --- a/akka-camel/src/test/scala/ProducerTest.scala +++ b/akka-camel/src/test/scala/ProducerTest.scala @@ -33,6 +33,7 @@ class ProducerTest extends JUnitSuite { // // TODO: test replies to messages sent with ! (bang) + // TODO: test copying of custom message headers // @Test def shouldProduceMessageSyncAndReceiveResponse = { diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala index 58ccc76273..8fafea4687 100644 --- a/akka-camel/src/test/scala/service/CamelServiceTest.scala +++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala @@ -2,12 +2,12 @@ package se.scalablesolutions.akka.camel.service import org.apache.camel.builder.RouteBuilder import org.junit.Assert._ -import org.junit.{Before, After, Test} import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.annotation.consume import se.scalablesolutions.akka.camel.{CamelContextManager, Consumer, Message} +import org.junit.{Ignore, Before, After, Test} class CamelServiceTest extends JUnitSuite with CamelService { @@ -23,26 +23,40 @@ class CamelServiceTest extends JUnitSuite with CamelService { var actor3: Actor = _ @Before def setUp = { + // register actors before starting the CamelService actor1 = new TestActor1().start actor2 = new TestActor2().start actor3 = new TestActor3().start - init() + // initialize global CamelContext + init + // customize global CamelContext context.addRoutes(new TestRouteBuilder) - onLoad + consumerPublisher.expectPublishCount(2) + load + consumerPublisher.awaitPublish } @After def tearDown = { - onUnload + unload actor1.stop actor2.stop actor3.stop } - @Test def shouldReceiveResponseViaGeneratedRoute = { + @Test def shouldReceiveResponseViaPreStartGeneratedRoutes = { assertEquals("Hello Martin (actor1)", template.requestBody("direct:actor1", "Martin")) assertEquals("Hello Martin (actor2)", template.requestBody("direct:actor2", "Martin")) } + @Test def shouldReceiveResponseViaPostStartGeneratedRoute = { + consumerPublisher.expectPublishCount(1) + // register actor after starting CamelService + val actor4 = new TestActor4().start + consumerPublisher.awaitPublish + assertEquals("Hello Martin (actor4)", template.requestBody("direct:actor4", "Martin")) + actor4.stop + } + @Test def shouldReceiveResponseViaCustomRoute = { assertEquals("Hello Tester (actor3)", template.requestBody("direct:actor3", "Martin")) } @@ -55,7 +69,6 @@ class TestActor1 extends Actor with Consumer { protected def receive = { case msg: Message => reply("Hello %s (actor1)" format msg.body) } - } @consume("direct:actor2") @@ -73,6 +86,14 @@ class TestActor3 extends Actor { } } +class TestActor4 extends Actor with Consumer { + def endpointUri = "direct:actor4" + + protected def receive = { + case msg: Message => reply("Hello %s (actor4)" format msg.body) + } +} + class TestRouteBuilder extends RouteBuilder { def configure { val actorUri = "actor:%s" format classOf[TestActor3].getName diff --git a/akka-core/src/main/scala/actor/ActorRegistry.scala b/akka-core/src/main/scala/actor/ActorRegistry.scala index 9e0b1cba08..6db4d0375a 100644 --- a/akka-core/src/main/scala/actor/ActorRegistry.scala +++ b/akka-core/src/main/scala/actor/ActorRegistry.scala @@ -8,8 +8,7 @@ import se.scalablesolutions.akka.util.Logging import scala.collection.mutable.ListBuffer import scala.reflect.Manifest - -import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.{CopyOnWriteArrayList, ConcurrentHashMap} /** * Registry holding all Actor instances in the whole system. @@ -23,9 +22,10 @@ import java.util.concurrent.ConcurrentHashMap * @author Jonas Bonér */ object ActorRegistry extends Logging { - private val actorsByUUID = new ConcurrentHashMap[String, Actor] - private val actorsById = new ConcurrentHashMap[String, List[Actor]] - private val actorsByClassName = new ConcurrentHashMap[String, List[Actor]] + private val actorsByUUID = new ConcurrentHashMap[String, Actor] + private val actorsById = new ConcurrentHashMap[String, List[Actor]] + private val actorsByClassName = new ConcurrentHashMap[String, List[Actor]] + private val registrationListeners = new CopyOnWriteArrayList[Actor] /** * Returns all actors in the system. @@ -103,6 +103,9 @@ object ActorRegistry extends Logging { if (actorsByClassName.containsKey(className)) { actorsByClassName.put(className, actor :: actorsByClassName.get(className)) } else actorsByClassName.put(className, actor :: Nil) + + // notify listeners + foreachListener(_.!(ActorRegistered(actor))(None)) } /** @@ -112,6 +115,8 @@ object ActorRegistry extends Logging { actorsByUUID remove actor.uuid actorsById remove actor.getId actorsByClassName remove actor.getClass.getName + // notify listeners + foreachListener(_.!(ActorUnregistered(actor))(None)) } /** @@ -125,4 +130,26 @@ object ActorRegistry extends Logging { actorsByClassName.clear log.info("All actors have been shut down and unregistered from ActorRegistry") } + + /** + * Adds the registration listener this this registry's listener list. + */ + def addRegistrationListener(listener: Actor) = { + registrationListeners.add(listener) + } + + /** + * Removes the registration listener this this registry's listener list. + */ + def removeRegistrationListener(listener: Actor) = { + registrationListeners.remove(listener) + } + + private def foreachListener(f: (Actor) => Unit) { + val iterator = registrationListeners.iterator + while (iterator.hasNext) f(iterator.next) + } } + +case class ActorRegistered(actor: Actor) +case class ActorUnregistered(actor: Actor) \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala new file mode 100644 index 0000000000..51c3940991 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -0,0 +1,57 @@ +package sample.camel + +import se.scalablesolutions.akka.actor.{Actor, RemoteActor} +import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.camel.{Message, Consumer} +import se.scalablesolutions.akka.util.Logging + +/** + * Client-initiated remote actor. + */ +class RemoteActor1 extends RemoteActor("localhost", 7777) with Consumer { + def endpointUri = "jetty:http://localhost:6644/remote1" + + protected def receive = { + case msg => reply("response from remote actor 1") + } +} + +/** + * Server-initiated remote actor. + */ +class RemoteActor2 extends Actor with Consumer { + def endpointUri = "jetty:http://localhost:6644/remote2" + + protected def receive = { + case msg => reply("response from remote actor 2") + } +} + +class Consumer1 extends Actor with Consumer with Logging { + def endpointUri = "file:data/input" + + def receive = { + case msg: Message => log.info("received %s" format msg.bodyAs(classOf[String])) + } +} + +@consume("jetty:http://0.0.0.0:8877/camel/test1") +class Consumer2 extends Actor { + def receive = { + case msg: Message => reply("Hello %s" format msg.bodyAs(classOf[String])) + } +} + +class Consumer3(transformer: Actor) extends Actor with Consumer { + def endpointUri = "jetty:http://0.0.0.0:8877/camel/welcome" + + def receive = { + case msg: Message => transformer.forward(msg.setBodyAs(classOf[String])) + } +} + +class Transformer(producer: Actor) extends Actor { + protected def receive = { + case msg: Message => producer.forward(msg.transformBody[String]("- %s -" format _)) + } +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Application1.scala b/akka-samples/akka-sample-camel/src/main/scala/Application1.scala new file mode 100644 index 0000000000..5b708bfac5 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Application1.scala @@ -0,0 +1,26 @@ +package sample.camel + +import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.remote.RemoteClient +/** + * @author Martin Krasser + */ +object Application1 { + + // + // TODO: completion of example + // + + def main(args: Array[String]) { + implicit val sender: Option[Actor] = None + + val actor1 = new RemoteActor1 + val actor2 = RemoteClient.actorFor("remote2", "localhost", 7777) + + actor1.start + + actor1 ! "hello" + actor2 ! "hello" + } + +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Application2.scala b/akka-samples/akka-sample-camel/src/main/scala/Application2.scala new file mode 100644 index 0000000000..83c6e8c439 --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/scala/Application2.scala @@ -0,0 +1,22 @@ +package sample.camel + +import se.scalablesolutions.akka.camel.service.CamelService +import se.scalablesolutions.akka.remote.RemoteNode + +/** + * @author Martin Krasser + */ +object Application2 { + + // + // TODO: completion of example + // + + def main(args: Array[String]) { + val camelService = CamelService.newInstance + camelService.load + RemoteNode.start("localhost", 7777) + RemoteNode.register("remote2", new RemoteActor2().start) + } + +} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index b23c99dafa..81af9775cb 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -33,7 +33,6 @@ class Boot { } class CustomRouteBuilder extends RouteBuilder { - def configure { val actorUri = "actor:%s" format classOf[Consumer2].getName from("jetty:http://0.0.0.0:8877/camel/test2").to(actorUri) @@ -42,7 +41,5 @@ class CustomRouteBuilder extends RouteBuilder { exchange.getOut.setBody("Welcome %s" format exchange.getIn.getBody) } }) - } - } \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala deleted file mode 100644 index b292d6e186..0000000000 --- a/akka-samples/akka-sample-camel/src/main/scala/Consumer1.scala +++ /dev/null @@ -1,18 +0,0 @@ -package sample.camel - -import se.scalablesolutions.akka.util.Logging -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.{Message, Consumer} - -/** - * @author Martin Krasser - */ -class Consumer1 extends Actor with Consumer with Logging { - - def endpointUri = "file:data/input" - - def receive = { - case msg: Message => log.info("received %s" format msg.bodyAs(classOf[String])) - } - -} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala deleted file mode 100644 index 4940c46f0d..0000000000 --- a/akka-samples/akka-sample-camel/src/main/scala/Consumer2.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sample.camel - -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.annotation.consume -import se.scalablesolutions.akka.camel.Message - -/** - * @author Martin Krasser - */ -@consume("jetty:http://0.0.0.0:8877/camel/test1") -class Consumer2 extends Actor { - - def receive = { - case msg: Message => reply("Hello %s" format msg.bodyAs(classOf[String])) - } - -} \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala b/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala deleted file mode 100644 index 39cf1f0652..0000000000 --- a/akka-samples/akka-sample-camel/src/main/scala/Consumer3.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sample.camel - -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.{Message, Consumer} - -/** - * @author Martin Krasser - */ -class Consumer3(transformer: Actor) extends Actor with Consumer { - - def endpointUri = "jetty:http://0.0.0.0:8877/camel/welcome" - - def receive = { - case msg: Message => transformer.forward(msg.setBodyAs(classOf[String])) - } - -} diff --git a/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala b/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala deleted file mode 100644 index 0df05c594c..0000000000 --- a/akka-samples/akka-sample-camel/src/main/scala/Transformer.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sample.camel - -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.Message - -/** - * @author Martin Krasser - */ -class Transformer(producer: Actor) extends Actor { - - protected def receive = { - case msg: Message => producer.forward(msg.transformBody[String]("- %s -" format _)) - } - -} \ No newline at end of file From 6cadb0d03526ea93d42d1a86b82887606b90b7fb Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 13 Mar 2010 14:06:52 +0100 Subject: [PATCH 53/81] Fixing container detection for SBT console mode --- akka-comet/src/main/scala/BootableCometActorService.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/akka-comet/src/main/scala/BootableCometActorService.scala b/akka-comet/src/main/scala/BootableCometActorService.scala index f6895aeee9..496cc33aed 100644 --- a/akka-comet/src/main/scala/BootableCometActorService.scala +++ b/akka-comet/src/main/scala/BootableCometActorService.scala @@ -43,8 +43,7 @@ trait BootableCometActorService extends Bootable with Logging { adapter.setHandleStaticResources(true) adapter.setServletInstance(new AkkaServlet) adapter.setContextPath(uri.getPath) - //Using autodetection for now - //adapter.addInitParameter("cometSupport", "org.atmosphere.container.GrizzlyCometSupport") + adapter.addInitParameter("cometSupport", "org.atmosphere.container.GrizzlyCometSupport") if (HOME.isDefined) adapter.setRootFolder(HOME.get + "/deploy/root") log.info("REST service root path [%s] and context path [%s]", adapter.getRootFolder, adapter.getContextPath) From 81b35c1f7d122f1e0a7026db52476649a2d470f9 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 13 Mar 2010 14:07:37 +0100 Subject: [PATCH 54/81] Return 408 is authentication times out --- akka-security/src/main/scala/Security.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/akka-security/src/main/scala/Security.scala b/akka-security/src/main/scala/Security.scala index 1f0d8a94aa..8a144f4282 100644 --- a/akka-security/src/main/scala/Security.scala +++ b/akka-security/src/main/scala/Security.scala @@ -87,10 +87,11 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging { override def filter(request: ContainerRequest): ContainerRequest = rolesAllowed match { case Some(roles) => { - (authenticator !! (Authenticate(request, roles), 10000)).get.asInstanceOf[AnyRef] match { - case OK => request - case r if r.isInstanceOf[Response] => + (authenticator.!![AnyRef](Authenticate(request, roles), 10000)) match { + case Some(OK) => request + case Some(r) if r.isInstanceOf[Response] => throw new WebApplicationException(r.asInstanceOf[Response]) + case None => throw new WebApplicationException(408) case x => { log.error("Authenticator replied with unexpected result [%s]", x); throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR) From d1a9e4a2861c6ebbf258250a44a38eb7147c961e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 13 Mar 2010 14:08:02 +0100 Subject: [PATCH 55/81] Fixed deprecation warning --- akka-core/src/test/scala/ThreadBasedDispatcherTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-core/src/test/scala/ThreadBasedDispatcherTest.scala b/akka-core/src/test/scala/ThreadBasedDispatcherTest.scala index b9663352c7..c848c56991 100644 --- a/akka-core/src/test/scala/ThreadBasedDispatcherTest.scala +++ b/akka-core/src/test/scala/ThreadBasedDispatcherTest.scala @@ -78,7 +78,7 @@ class ThreadBasedDispatcherTest extends JUnitSuite { }) dispatcher.start for (i <- 0 until 100) { - dispatcher.dispatch(new MessageInvocation(key1, new Integer(i), None, None, None)) + dispatcher.dispatch(new MessageInvocation(key1, i, None, None, None)) } assert(handleLatch.await(5, TimeUnit.SECONDS)) assert(!threadingIssueDetected.get) From 7e517ede0d6748c510b8c0bbd642ab0b2cd772d1 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 13 Mar 2010 14:08:30 +0100 Subject: [PATCH 56/81] Revert to Atmosphere 0.5.4 because of issue in 0.6-SNAPSHOT --- project/build/AkkaProject.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 2df647a5bd..c3c7211d97 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -42,7 +42,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // project versions val JERSEY_VERSION = "1.1.5" - val ATMO_VERSION = "0.6-SNAPSHOT" + val ATMO_VERSION = "0.5.4" val CASSANDRA_VERSION = "0.5.0" // ------------------------------------------------------------ From e97e944613306212a473075ddff61f2ac4167465 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Sun, 14 Mar 2010 11:17:34 +0100 Subject: [PATCH 57/81] publish/subscribe examples using jms and cometd --- akka-camel/pom.xml | 47 +++++++++++++++++++ .../main/resources/sample-camel-context.xml | 23 +++++++++ .../src/main/scala/Actors.scala | 41 ++++++++++++++-- .../src/main/scala/Application1.scala | 6 ++- .../src/main/scala/Boot.scala | 29 +++++++++++- .../src/main/scala/Producer1.scala | 17 ------- 6 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 akka-samples/akka-sample-camel/src/main/resources/sample-camel-context.xml delete mode 100644 akka-samples/akka-sample-camel/src/main/scala/Producer1.scala diff --git a/akka-camel/pom.xml b/akka-camel/pom.xml index bc42439e84..f11d2ad46e 100644 --- a/akka-camel/pom.xml +++ b/akka-camel/pom.xml @@ -27,10 +27,57 @@ camel-core 2.2.0 + + + org.apache.camel camel-jetty 2.2.0 + + + + org.mortbay.jetty + jetty + + + org.mortbay.jetty + jetty-client + + + + + + org.mortbay.jetty + jetty + 6.1.11 + + + org.mortbay.jetty + jetty-client + 6.1.11 + + + org.apache.camel + camel-cometd + 2.2.0 + + + org.apache.camel + camel-jms + 2.2.0 + + + org.apache.activemq + activemq-core + 5.3.0 diff --git a/akka-samples/akka-sample-camel/src/main/resources/sample-camel-context.xml b/akka-samples/akka-sample-camel/src/main/resources/sample-camel-context.xml new file mode 100644 index 0000000000..b3d811d8de --- /dev/null +++ b/akka-samples/akka-sample-camel/src/main/resources/sample-camel-context.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala index 51c3940991..11367dedb4 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -2,7 +2,7 @@ package sample.camel import se.scalablesolutions.akka.actor.{Actor, RemoteActor} import se.scalablesolutions.akka.annotation.consume -import se.scalablesolutions.akka.camel.{Message, Consumer} +import se.scalablesolutions.akka.camel.{Producer, Message, Consumer} import se.scalablesolutions.akka.util.Logging /** @@ -12,7 +12,7 @@ class RemoteActor1 extends RemoteActor("localhost", 7777) with Consumer { def endpointUri = "jetty:http://localhost:6644/remote1" protected def receive = { - case msg => reply("response from remote actor 1") + case msg: Message => reply(Message("hello %s" format msg.body, Map("sender" -> "remote1"))) } } @@ -23,10 +23,19 @@ class RemoteActor2 extends Actor with Consumer { def endpointUri = "jetty:http://localhost:6644/remote2" protected def receive = { - case msg => reply("response from remote actor 2") + case msg: Message => reply(Message("hello %s" format msg.body, Map("sender" -> "remote2"))) } } +class Producer1 extends Actor with Producer { + def endpointUri = "direct:welcome" + + override def oneway = false // default + override def async = true // default + + protected def receive = produce +} + class Consumer1 extends Actor with Consumer with Logging { def endpointUri = "file:data/input" @@ -54,4 +63,30 @@ class Transformer(producer: Actor) extends Actor { protected def receive = { case msg: Message => producer.forward(msg.transformBody[String]("- %s -" format _)) } +} + +class Subscriber(name:String, uri: String) extends Actor with Consumer { + def endpointUri = uri + + protected def receive = { + case msg: Message => log.info("%s received: %s" format (name, msg.body)) + } +} + +class Publisher(name: String, uri: String) extends Actor with Producer { + id = name + def endpointUri = uri + override def oneway = true + protected def receive = produce +} + +class PublisherBridge(uri: String, publisher: Actor) extends Actor with Consumer { + def endpointUri = uri + + protected def receive = { + case msg: Message => { + publisher ! msg.bodyAs(classOf[String]) + reply("message published") + } + } } \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Application1.scala b/akka-samples/akka-sample-camel/src/main/scala/Application1.scala index 5b708bfac5..4a55f2014f 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Application1.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Application1.scala @@ -1,7 +1,9 @@ package sample.camel import se.scalablesolutions.akka.actor.Actor +import se.scalablesolutions.akka.camel.Message import se.scalablesolutions.akka.remote.RemoteClient + /** * @author Martin Krasser */ @@ -19,8 +21,8 @@ object Application1 { actor1.start - actor1 ! "hello" - actor2 ! "hello" + println(actor1 !! Message("actor1")) + println(actor2 !! Message("actor2")) } } \ No newline at end of file diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index 81af9775cb..42cb367076 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -1,7 +1,10 @@ package sample.camel -import org.apache.camel.builder.RouteBuilder import org.apache.camel.{Exchange, Processor} +import org.apache.camel.builder.RouteBuilder +import org.apache.camel.impl.DefaultCamelContext +import org.apache.camel.spring.spi.ApplicationContextRegistry +import org.springframework.context.support.ClassPathXmlApplicationContext import se.scalablesolutions.akka.actor.SupervisorFactory import se.scalablesolutions.akka.camel.CamelContextManager @@ -12,9 +15,15 @@ import se.scalablesolutions.akka.config.ScalaConfig._ */ class Boot { - CamelContextManager.init() + // Create CamelContext with Spring-based registry and custom route builder + + val context = new ClassPathXmlApplicationContext("/sample-camel-context.xml", getClass) + val registry = new ApplicationContextRegistry(context) + CamelContextManager.init(new DefaultCamelContext(registry)) CamelContextManager.context.addRoutes(new CustomRouteBuilder) + // Basic example + val factory = SupervisorFactory( SupervisorConfig( RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), @@ -22,6 +31,8 @@ class Boot { Supervise(new Consumer2, LifeCycle(Permanent)) :: Nil)) factory.newInstance.start + // Routing example + val producer = new Producer1 val mediator = new Transformer(producer) val consumer = new Consumer3(mediator) @@ -30,6 +41,20 @@ class Boot { mediator.start consumer.start + // Publish subscribe example + + val cometdUri = "cometd://localhost:8111/test/abc?resourceBase=target" + val cometdSubscriber = new Subscriber("cometd-subscriber", cometdUri).start + val cometdPublisher = new Publisher("cometd-publisher", cometdUri).start + + val jmsUri = "jms:topic:test" + val jmsSubscriber1 = new Subscriber("jms-subscriber-1", jmsUri).start + val jmsSubscriber2 = new Subscriber("jms-subscriber-2", jmsUri).start + val jmsPublisher = new Publisher("jms-publisher", jmsUri).start + + val cometdPublisherBridge = new PublisherBridge("jetty:http://0.0.0.0:8877/camel/pub/cometd", cometdPublisher).start + val jmsPublisherBridge = new PublisherBridge("jetty:http://0.0.0.0:8877/camel/pub/jms", jmsPublisher).start + } class CustomRouteBuilder extends RouteBuilder { diff --git a/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala b/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala deleted file mode 100644 index 11151a58df..0000000000 --- a/akka-samples/akka-sample-camel/src/main/scala/Producer1.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sample.camel - -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.Producer - -/** - * @author Martin Krasser - */ -class Producer1 extends Actor with Producer { - - def endpointUri = "direct:welcome" - - override def oneway = false // default - override def async = true // default - - protected def receive = produce -} \ No newline at end of file From d569b29a8bfd04241bc93b6293243f6456cf8106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Sun, 14 Mar 2010 18:01:49 +0100 Subject: [PATCH 58/81] dispatcher speed improvements --- .../ExecutorBasedEventDrivenDispatcher.scala | 13 +++++++------ project/build/AkkaProject.scala | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala index e115800d4b..68e537ae82 100644 --- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -62,13 +62,14 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche def dispatch(invocation: MessageInvocation) = if (active) { executor.execute(new Runnable() { def run = { - invocation.receiver.synchronized { +// invocation.receiver.synchronized { var messageInvocation = invocation.receiver._mailbox.poll - while (messageInvocation != null) { - messageInvocation.invoke - messageInvocation = invocation.receiver._mailbox.poll - } - } + if (messageInvocation != null) messageInvocation.invoke +// while (messageInvocation != null) { +// messageInvocation.invoke +// messageInvocation = invocation.receiver._mailbox.poll +// } +// } } }) } else throw new IllegalStateException("Can't submit invocations to dispatcher since it's not started") diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 2df647a5bd..d6f2a1d79d 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -127,9 +127,10 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { Credentials(Path.userHome / ".akka_publish_credentials", log) override def managedStyle = ManagedStyle.Maven - val publishTo = "Scalable Solutions Maven Repository" at "http://scalablesolutions.se/akka/repository/" + val publishTo = "Scalable Solutions Maven Repository" at "~/tmp/akka" +// val publishTo = "Scalable Solutions Maven Repository" at "http://scalablesolutions.se/akka/repository/" val sourceArtifact = Artifact(artifactID, "src", "jar", Some("sources"), Nil, None) - val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None) +// val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None) override def packageDocsJar = defaultJarPath("-javadoc.jar") override def packageSrcJar= defaultJarPath("-sources.jar") From ae0ef2d06c313e813fb995bb5cac420cca03d2dc Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Mon, 15 Mar 2010 11:08:25 +0100 Subject: [PATCH 59/81] OS-specific substring search in paths (fixes 'sbt dist' issue on Windows) --- project/build/AkkaProject.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index c3c7211d97..891126f22e 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -358,7 +358,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { def deployTask(info: ProjectInfo, toDir: Path) = task { val projectPath = info.projectPath.toString - val moduleName = projectPath.substring(projectPath.lastIndexOf('/') + 1, projectPath.length) + val moduleName = projectPath.substring(projectPath.lastIndexOf(System.getProperty("file.separator")) + 1, projectPath.length) // FIXME need to find out a way to grab these paths from the sbt system val JAR_FILE_NAME = moduleName + "_%s-%s.jar".format(defScalaVersion.value, version) val JAR_FILE_PATH = projectPath + "/target/scala_%s/".format(defScalaVersion.value) + JAR_FILE_NAME From 7eb4245390be61a8156fb1c92379ea860bdc27a7 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Mon, 15 Mar 2010 14:38:54 +0100 Subject: [PATCH 60/81] prepare merge with master --- .../akka-sample-lift/config/akka.conf | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/akka-samples/akka-sample-lift/config/akka.conf b/akka-samples/akka-sample-lift/config/akka.conf index 4a02b208bb..55334c6dbd 100644 --- a/akka-samples/akka-sample-lift/config/akka.conf +++ b/akka-samples/akka-sample-lift/config/akka.conf @@ -1,64 +1,64 @@ -##################### -# Akka Config File # -################### - -# This file has all the default settings, so all these could be removed with no visible effect. -# Modify as needed. - - - filename = "./logs/akka.log" - roll = "daily" # Options: never, hourly, daily, sunday/monday/... - level = "debug" # Options: fatal, critical, error, warning, info, debug, trace - console = on - # syslog_host = "" - # syslog_server_name = "" - - - - version = "0.7-SNAPSHOT" - - - timeout = 5000 # default timeout for future based invocations - concurrent-mode = off # if turned on, then the same actor instance is allowed to execute concurrently - - # e.g. departing from the actor model for better performance - serialize-messages = on # does a deep clone of (non-primitive) messages to ensure immutability - - - - service = on - restart-on-collision = off # (not implemented yet) if 'on' then it reschedules the transaction, - # if 'off' then throws an exception or rollback for user to handle - wait-for-completion = 100 # how long time in millis a transaction should be given time to complete when a collision is detected - wait-nr-of-times = 3 # the number of times it should check for completion of a pending transaction upon collision - distributed = off # not implemented yet - - - - service = on - hostname = "localhost" - port = 9999 - connection-timeout = 1000 # in millis - - - - service = on - hostname = "localhost" - port = 9998 - - - - system = "cassandra" # Options: cassandra (coming: terracotta, redis, tokyo-cabinet, tokyo-tyrant, voldemort, memcached, hazelcast) - - - service = on - storage-format = "java" # Options: java, scala-json, java-json - blocking = false # inserts and queries should be blocking or not - - - service = on - pidfile = "akka.pid" - - - - - +##################### +# Akka Config File # +################### + +# This file has all the default settings, so all these could be removed with no visible effect. +# Modify as needed. + + + filename = "./logs/akka.log" + roll = "daily" # Options: never, hourly, daily, sunday/monday/... + level = "debug" # Options: fatal, critical, error, warning, info, debug, trace + console = on + # syslog_host = "" + # syslog_server_name = "" + + + + version = "0.7-SNAPSHOT" + + + timeout = 5000 # default timeout for future based invocations + concurrent-mode = off # if turned on, then the same actor instance is allowed to execute concurrently - + # e.g. departing from the actor model for better performance + serialize-messages = on # does a deep clone of (non-primitive) messages to ensure immutability + + + + service = on + restart-on-collision = off # (not implemented yet) if 'on' then it reschedules the transaction, + # if 'off' then throws an exception or rollback for user to handle + wait-for-completion = 100 # how long time in millis a transaction should be given time to complete when a collision is detected + wait-nr-of-times = 3 # the number of times it should check for completion of a pending transaction upon collision + distributed = off # not implemented yet + + + + service = on + hostname = "localhost" + port = 9999 + connection-timeout = 1000 # in millis + + + + service = on + hostname = "localhost" + port = 9998 + + + + system = "cassandra" # Options: cassandra (coming: terracotta, redis, tokyo-cabinet, tokyo-tyrant, voldemort, memcached, hazelcast) + + + service = on + storage-format = "java" # Options: java, scala-json, java-json + blocking = false # inserts and queries should be blocking or not + + + service = on + pidfile = "akka.pid" + + + + + From 82f411a2d663fea54e6e780bd8c0fce55102975d Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Tue, 16 Mar 2010 06:45:04 +0100 Subject: [PATCH 61/81] Move to sbt --- akka-camel/pom.xml | 98 ------------------------- akka-kernel/src/main/scala/Kernel.scala | 4 +- akka-samples/akka-sample-camel/pom.xml | 39 ---------- project/build/AkkaProject.scala | 22 +++++- 4 files changed, 23 insertions(+), 140 deletions(-) delete mode 100644 akka-camel/pom.xml delete mode 100644 akka-samples/akka-sample-camel/pom.xml diff --git a/akka-camel/pom.xml b/akka-camel/pom.xml deleted file mode 100644 index f11d2ad46e..0000000000 --- a/akka-camel/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - 4.0.0 - - akka-camel - Akka Camel Module - - jar - - - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - - akka-core - ${project.groupId} - ${project.version} - - - org.apache.camel - camel-core - 2.2.0 - - - - - - org.apache.camel - camel-jetty - 2.2.0 - - - - org.mortbay.jetty - jetty - - - org.mortbay.jetty - jetty-client - - - - - - org.mortbay.jetty - jetty - 6.1.11 - - - org.mortbay.jetty - jetty-client - 6.1.11 - - - org.apache.camel - camel-cometd - 2.2.0 - - - org.apache.camel - camel-jms - 2.2.0 - - - org.apache.activemq - activemq-core - 5.3.0 - - - - - org.scalatest - scalatest - 1.0 - test - - - junit - junit - 4.5 - test - - - - \ No newline at end of file diff --git a/akka-kernel/src/main/scala/Kernel.scala b/akka-kernel/src/main/scala/Kernel.scala index d7c7a4b2a5..6c0cd87058 100644 --- a/akka-kernel/src/main/scala/Kernel.scala +++ b/akka-kernel/src/main/scala/Kernel.scala @@ -7,6 +7,7 @@ package se.scalablesolutions.akka.kernel import se.scalablesolutions.akka.remote.BootableRemoteActorService import se.scalablesolutions.akka.comet.BootableCometActorService import se.scalablesolutions.akka.actor.BootableActorLoaderService +import se.scalablesolutions.akka.camel.service.CamelService import se.scalablesolutions.akka.config.Config import se.scalablesolutions.akka.util.{Logging, Bootable} @@ -37,7 +38,8 @@ object Kernel extends Logging { def boot: Unit = boot(true, new BootableActorLoaderService with BootableRemoteActorService - with BootableCometActorService) + with BootableCometActorService + with CamelService) /** * Boots up the Kernel. diff --git a/akka-samples/akka-sample-camel/pom.xml b/akka-samples/akka-sample-camel/pom.xml deleted file mode 100644 index 95adba3149..0000000000 --- a/akka-samples/akka-sample-camel/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - 4.0.0 - - akka-sample-camel - Akka Camel Sample Module - - jar - - - akka-samples-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - src/main/scala - - - maven-antrun-plugin - - - install - - - - - - - run - - - - - - - - diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 891126f22e..dc10973b8a 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -79,12 +79,13 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) lazy val akka_rest = project("akka-rest", "akka-rest", new AkkaRestProject(_), akka_core) lazy val akka_comet = project("akka-comet", "akka-comet", new AkkaCometProject(_), akka_rest) + lazy val akka_camel = project("akka-camel", "akka-camel", new AkkaCamelProject(_), akka_core) lazy val akka_patterns = project("akka-patterns", "akka-patterns", new AkkaPatternsProject(_), akka_core) lazy val akka_security = project("akka-security", "akka-security", new AkkaSecurityProject(_), akka_core) lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_)) lazy val akka_cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_)) lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), - akka_core, akka_rest, akka_persistence, akka_cluster, akka_amqp, akka_security, akka_comet, akka_patterns) + akka_core, akka_rest, akka_persistence, akka_cluster, akka_amqp, akka_security, akka_comet, akka_camel, akka_patterns) // functional tests in java lazy val akka_fun_test = project("akka-fun-test-java", "akka-fun-test-java", new AkkaFunTestProject(_), akka_kernel) @@ -207,6 +208,11 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val dist = deployTask(info, distPath) dependsOn(`package`) describedAs("Deploying") } + class AkkaCamelProject(info: ProjectInfo) extends DefaultProject(info) { + val camel_core = "org.apache.camel" % "camel-core" % "2.2.0" % "compile" + lazy val dist = deployTask(info, distPath) dependsOn(`package`) describedAs("Deploying") + } + class AkkaPatternsProject(info: ProjectInfo) extends DefaultProject(info) { // testing val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test" @@ -305,7 +311,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "compile" // testing - val jetty = "org.mortbay.jetty" % "jetty" % "6.1.6" % "test" + val jetty = "org.mortbay.jetty" % "jetty" % "6.1.11" % "test" val junit = "junit" % "junit" % "4.5" % "test" lazy val dist = deployTask(info, deployPath) dependsOn(`package`) describedAs("Deploying") } @@ -319,6 +325,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val dist = deployTask(info, deployPath) dependsOn(`package`) describedAs("Deploying") } + class AkkaSampleCamelProject(info: ProjectInfo) extends DefaultProject(info) { + val jetty = "org.mortbay.jetty" % "jetty" % "6.1.11" % "compile" + val jetty_client = "org.mortbay.jetty" % "jetty-client" % "6.1.11" % "compile" + val camel_http = "org.apache.camel" % "camel-http" % "2.2.0" % "compile" + val camel_jetty = "org.apache.camel" % "camel-jetty" % "2.2.0" % "compile" intransitive() + val camel_jms = "org.apache.camel" % "camel-jms" % "2.2.0" % "compile" + val camel_cometd = "org.apache.camel" % "camel-cometd" % "2.2.0" % "compile" + val activemq_core = "org.apache.activemq" % "activemq-core" % "5.3.0" % "compile" + lazy val dist = deployTask(info, deployPath) dependsOn(`package`) describedAs("Deploying") + } + class AkkaSampleSecurityProject(info: ProjectInfo) extends DefaultProject(info) { val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" @@ -330,6 +347,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val akka_sample_lift = project("akka-sample-lift", "akka-sample-lift", new AkkaSampleLiftProject(_), akka_kernel) lazy val akka_sample_rest_java = project("akka-sample-rest-java", "akka-sample-rest-java", new AkkaSampleRestJavaProject(_), akka_kernel) lazy val akka_sample_rest_scala = project("akka-sample-rest-scala", "akka-sample-rest-scala", new AkkaSampleRestScalaProject(_), akka_kernel) + lazy val akka_sample_camel = project("akka-sample-camel", "akka-sample-camel", new AkkaSampleCamelProject(_), akka_kernel) lazy val akka_sample_security = project("akka-sample-security", "akka-sample-security", new AkkaSampleSecurityProject(_), akka_kernel) } From 1481d1d40cd639bd23028ecdf370ed6772b51243 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Tue, 16 Mar 2010 07:08:10 +0100 Subject: [PATCH 62/81] akka-camel added to manifest classpath. All examples enabled. --- config/akka-reference.conf | 10 ++++------ project/build/AkkaProject.scala | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 6152bc1abb..6c442e3ef1 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -19,12 +19,10 @@ # FQN to the class doing initial active object/actor # supervisor bootstrap, should be defined in default constructor - boot = ["sample.camel.Boot"] - - # Disable other boot configurations at the moment - #boot = ["sample.java.Boot", - # "sample.scala.Boot", - # "se.scalablesolutions.akka.security.samples.Boot"] + boot = ["sample.camel.Boot", + "sample.java.Boot", + "sample.scala.Boot", + "se.scalablesolutions.akka.security.samples.Boot"] timeout = 5000 # default timeout for future based invocations diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index dc10973b8a..6bb6757518 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -113,6 +113,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { " dist/akka-cluster-jgroups_%s-%s.jar".format(defScalaVersion.value, version) + " dist/akka-rest_%s-%s.jar".format(defScalaVersion.value, version) + " dist/akka-comet_%s-%s.jar".format(defScalaVersion.value, version) + + " dist/akka-camel_%s-%s.jar".format(defScalaVersion.value, version) + " dist/akka-security_%s-%s.jar".format(defScalaVersion.value, version) + " dist/akka-amqp_%s-%s.jar".format(defScalaVersion.value, version) + " dist/akka-patterns_%s-%s.jar".format(defScalaVersion.value, version) + From 823661c499349a9c32b6c1a68d2f83972de03dc3 Mon Sep 17 00:00:00 2001 From: Jan Van Besien Date: Tue, 16 Mar 2010 12:25:58 +0100 Subject: [PATCH 63/81] Fixed bug which allowed messages to be "missed" if they arrived after looping through the mailbox, but before releasing the lock. --- .../ExecutorBasedEventDrivenDispatcher.scala | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 77879e0e3b..b48e7717cf 100644 --- a/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-core/src/main/scala/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -62,19 +62,24 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche def dispatch(invocation: MessageInvocation) = if (active) { executor.execute(new Runnable() { def run = { - val lockedForDispatching = invocation.receiver._dispatcherLock.tryLock - if (lockedForDispatching) { - try { - // Only dispatch if we got the lock. Otherwise another thread is already dispatching. - var messageInvocation = invocation.receiver._mailbox.poll - while (messageInvocation != null) { - messageInvocation.invoke - messageInvocation = invocation.receiver._mailbox.poll + var lockAcquiredOnce = false + // this do-wile loop is required to prevent missing new messages between the end of the inner while + // loop and releasing the lock + do { + if (invocation.receiver._dispatcherLock.tryLock) { + lockAcquiredOnce = true + try { + // Only dispatch if we got the lock. Otherwise another thread is already dispatching. + var messageInvocation = invocation.receiver._mailbox.poll + while (messageInvocation != null) { + messageInvocation.invoke + messageInvocation = invocation.receiver._mailbox.poll + } + } finally { + invocation.receiver._dispatcherLock.unlock } - } finally { - invocation.receiver._dispatcherLock.unlock } - } + } while ((lockAcquiredOnce && !invocation.receiver._mailbox.isEmpty)) } }) } else throw new IllegalStateException("Can't submit invocations to dispatcher since it's not started") @@ -94,4 +99,4 @@ class ExecutorBasedEventDrivenDispatcher(_name: String) extends MessageDispatche "Can't build a new thread pool for a dispatcher that is already up and running") private[akka] def init = withNewThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity.buildThreadPool -} \ No newline at end of file +} From 08e3a6156dccabb8ac7a175ae5ab5878f44d95d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Tue, 16 Mar 2010 22:54:48 +0100 Subject: [PATCH 64/81] Minor syntax edits --- .../main/scala/CamelContextLifecycle.scala | 8 ++--- akka-camel/src/main/scala/Consumer.scala | 5 +-- akka-camel/src/main/scala/Message.scala | 31 ++++++++-------- akka-camel/src/main/scala/Producer.scala | 35 +++++++------------ .../main/scala/component/ActorComponent.scala | 19 +++++----- .../src/main/scala/service/CamelService.scala | 9 ++--- .../scala/service/ConsumerPublisher.scala | 32 ++++++----------- akka-core/src/main/scala/actor/Actor.scala | 1 - 8 files changed, 57 insertions(+), 83 deletions(-) diff --git a/akka-camel/src/main/scala/CamelContextLifecycle.scala b/akka-camel/src/main/scala/CamelContextLifecycle.scala index 68e67a0612..b9a696207c 100644 --- a/akka-camel/src/main/scala/CamelContextLifecycle.scala +++ b/akka-camel/src/main/scala/CamelContextLifecycle.scala @@ -51,7 +51,7 @@ trait CamelContextLifecycle extends Logging { /** * Starts the CamelContext and ProducerTemplate. */ - def start() { + def start = { context.start template.start _started = true @@ -61,7 +61,7 @@ trait CamelContextLifecycle extends Logging { /** * Stops the CamelContext and ProducerTemplate. */ - def stop() { + def stop = { template.stop context.stop _initialized = false @@ -72,9 +72,7 @@ trait CamelContextLifecycle extends Logging { /** * Initializes this lifecycle object with the a DefaultCamelContext. */ - def init() { - init(new DefaultCamelContext) - } + def init: Unit = init(new DefaultCamelContext) /** * Initializes this lifecycle object with the given CamelContext. diff --git a/akka-camel/src/main/scala/Consumer.scala b/akka-camel/src/main/scala/Consumer.scala index 1a3003d863..27ec98b25d 100644 --- a/akka-camel/src/main/scala/Consumer.scala +++ b/akka-camel/src/main/scala/Consumer.scala @@ -11,13 +11,10 @@ import se.scalablesolutions.akka.actor.Actor * * @author Martin Krasser */ -trait Consumer { - - self: Actor => +trait Consumer { self: Actor => /** * Returns the Camel endpoint URI to consume messages from. */ def endpointUri: String - } \ No newline at end of file diff --git a/akka-camel/src/main/scala/Message.scala b/akka-camel/src/main/scala/Message.scala index b145ff10cd..8e0156c669 100644 --- a/akka-camel/src/main/scala/Message.scala +++ b/akka-camel/src/main/scala/Message.scala @@ -30,16 +30,13 @@ case class Message(val body: Any, val headers: Map[String, Any]) { * * @see CamelContextManager. */ - def bodyAs[T](clazz: Class[T]): T = { + def bodyAs[T](clazz: Class[T]): T = CamelContextManager.context.getTypeConverter.mandatoryConvertTo[T](clazz, body) - } /** * Returns those headers from this message whose name is contained in names. */ - def headers(names: Set[String]): Map[String, Any] = { - headers.filter(names contains _._1) - } + def headers(names: Set[String]): Map[String, Any] = headers.filter(names contains _._1) /** * Creates a Message with a new body using a transformer function. @@ -86,13 +83,14 @@ case class Message(val body: Any, val headers: Map[String, Any]) { * @author Martin Krasser */ object Message { + /** * Message header to correlate request with response messages. Applications that send * messages to a Producer actor may want to set this header on the request message * so that it can be correlated with an asynchronous response. Messages send to Consumer * actors have this header already set. */ - val MessageExchangeId = "MessageExchangeId" + val MessageExchangeId = "MessageExchangeId".intern /** * Creates a new Message with body as message body and an empty header map. @@ -189,7 +187,8 @@ class CamelExchangeAdapter(exchange: Exchange) { * * @see Failure */ - def toFailureMessage(headers: Map[String, Any]): Failure = Failure(exchange.getException, headers ++ responseMessage.toMessage.headers) + def toFailureMessage(headers: Map[String, Any]): Failure = + Failure(exchange.getException, headers ++ responseMessage.toMessage.headers) private def requestMessage = exchange.getIn @@ -223,28 +222,28 @@ class CamelMessageAdapter(val cm: CamelMessage) { * @param headers additional headers to set on the created Message in addition to those * in the Camel message. */ - def toMessage(headers: Map[String, Any]): Message = { - Message(cm.getBody, cmHeaders(headers, cm)) - } + def toMessage(headers: Map[String, Any]): Message = Message(cm.getBody, cmHeaders(headers, cm)) - private def cmHeaders(headers: Map[String, Any], cm: CamelMessage) = { + private def cmHeaders(headers: Map[String, Any], cm: CamelMessage) = headers ++ MapWrapper[String, AnyRef](cm.getHeaders).elements - } - } /** - * Defines conversion methods to CamelExchangeAdapter and CamelMessageAdapter. Imported by applications + * Defines conversion methods to CamelExchangeAdapter and CamelMessageAdapter. + * Imported by applications * that implicitly want to use conversion methods of CamelExchangeAdapter and CamelMessageAdapter. */ object CamelMessageConversion { + /** * Creates an CamelExchangeAdapter for the given Camel exchange. */ - implicit def toExchangeAdapter(ce: Exchange): CamelExchangeAdapter = new CamelExchangeAdapter(ce) + implicit def toExchangeAdapter(ce: Exchange): CamelExchangeAdapter = + new CamelExchangeAdapter(ce) /** * Creates an CamelMessageAdapter for the given Camel message. */ - implicit def toMessageAdapter(cm: CamelMessage): CamelMessageAdapter = new CamelMessageAdapter(cm) + implicit def toMessageAdapter(cm: CamelMessage): CamelMessageAdapter = + new CamelMessageAdapter(cm) } \ No newline at end of file diff --git a/akka-camel/src/main/scala/Producer.scala b/akka-camel/src/main/scala/Producer.scala index 4c0e42a69f..43e9b8b10e 100644 --- a/akka-camel/src/main/scala/Producer.scala +++ b/akka-camel/src/main/scala/Producer.scala @@ -19,9 +19,7 @@ import se.scalablesolutions.akka.util.Logging * * @author Martin Krasser */ -trait Producer { - - self: Actor => +trait Producer { self: Actor => private val headersToCopyDefault = Set(Message.MessageExchangeId) @@ -68,9 +66,8 @@ trait Producer { * @param msg: the message to produce. The message is converted to its canonical * representation via Message.canonicalize. */ - protected def produceOneway(msg: Any): Unit = { + protected def produceOneway(msg: Any): Unit = template.send(endpointUri, createInOnlyExchange.fromRequestMessage(Message.canonicalize(msg))) - } /** * Initiates a one-way (in-only) message exchange to the Camel endpoint given by @@ -80,9 +77,9 @@ trait Producer { * @param msg: the message to produce. The message is converted to its canonical * representation via Message.canonicalize. */ - protected def produceOnewayAsync(msg: Any): Unit = { - template.asyncSend(endpointUri, createInOnlyExchange.fromRequestMessage(Message.canonicalize(msg))) - } + protected def produceOnewayAsync(msg: Any): Unit = + template.asyncSend( + endpointUri, createInOnlyExchange.fromRequestMessage(Message.canonicalize(msg))) /** * Initiates a two-way (in-out) message exchange to the Camel endpoint given by @@ -99,10 +96,8 @@ trait Producer { def process(exchange: Exchange) = exchange.fromRequestMessage(cmsg) } val result = template.request(endpointUri, requestProcessor) - if (result.isFailed) - result.toFailureMessage(cmsg.headers(headersToCopy)) - else - result.toResponseMessage(cmsg.headers(headersToCopy)) + if (result.isFailed) result.toFailureMessage(cmsg.headers(headersToCopy)) + else result.toResponseMessage(cmsg.headers(headersToCopy)) } /** @@ -118,7 +113,8 @@ trait Producer { */ protected def produceAsync(msg: Any): Unit = { val cmsg = Message.canonicalize(msg) - val sync = new ProducerResponseSender(cmsg.headers(headersToCopy), this.sender, this.senderFuture, this) + val sync = new ProducerResponseSender( + cmsg.headers(headersToCopy), this.sender, this.senderFuture, this) template.asyncCallback(endpointUri, createInOutExchange.fromRequestMessage(cmsg), sync) } @@ -154,9 +150,8 @@ trait Producer { * * @see CamelContextManager. */ - protected def createExchange(pattern: ExchangePattern): Exchange = { + protected def createExchange(pattern: ExchangePattern): Exchange = new DefaultExchange(CamelContextManager.context, pattern) - } } /** @@ -177,24 +172,20 @@ class ProducerResponseSender( * Replies a Failure message, created from the given exchange, to sender (or * senderFuture if applicable). */ - def onFailure(exchange: Exchange) = { - reply(exchange.toFailureMessage(headers)) - } + def onFailure(exchange: Exchange) = reply(exchange.toFailureMessage(headers)) /** * Replies a response Message, created from the given exchange, to sender (or * senderFuture if applicable). */ - def onComplete(exchange: Exchange) = { - reply(exchange.toResponseMessage(headers)) - } + def onComplete(exchange: Exchange) = reply(exchange.toResponseMessage(headers)) private def reply(message: Any) = { sender match { case Some(actor) => actor ! message case None => senderFuture match { case Some(future) => future.completeWithResult(message) - case None => log.warning("no destination for sending response") + case None => log.warning("No destination for sending response") } } } diff --git a/akka-camel/src/main/scala/component/ActorComponent.scala b/akka-camel/src/main/scala/component/ActorComponent.scala index 5788fd9028..763f9dd017 100644 --- a/akka-camel/src/main/scala/component/ActorComponent.scala +++ b/akka-camel/src/main/scala/component/ActorComponent.scala @@ -52,7 +52,10 @@ class ActorComponent extends DefaultComponent { * @author Martin Krasser */ -class ActorEndpoint(uri: String, comp: ActorComponent, val id: Option[String], val uuid: Option[String]) extends DefaultEndpoint(uri, comp) { +class ActorEndpoint(uri: String, + comp: ActorComponent, + val id: Option[String], + val uuid: Option[String]) extends DefaultEndpoint(uri, comp) { /** * @throws UnsupportedOperationException @@ -95,19 +98,16 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { */ def process(exchange: Exchange) { val actor = target getOrElse (throw new ActorNotRegisteredException(ep.getEndpointUri)) - if (exchange.getPattern.isOutCapable) - processInOut(exchange, actor) - else - processInOnly(exchange, actor) + if (exchange.getPattern.isOutCapable) processInOut(exchange, actor) + else processInOnly(exchange, actor) } /** * Send the exchange in-message to the given actor using the ! operator. The message * send to the actor is of type se.scalablesolutions.akka.camel.Message. */ - protected def processInOnly(exchange: Exchange, actor: Actor) { + protected def processInOnly(exchange: Exchange, actor: Actor): Unit = actor ! exchange.toRequestMessage(Map(Message.MessageExchangeId -> exchange.getExchangeId)) - } /** * Send the exchange in-message to the given actor using the !! operator. The exchange @@ -128,10 +128,9 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { } } - private def target: Option[Actor] = { + private def target: Option[Actor] = if (ep.id.isDefined) targetById(ep.id.get) else targetByUuid(ep.uuid.get) - } private def targetById(id: String) = ActorRegistry.actorsFor(id) match { case Nil => None @@ -143,7 +142,7 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) { } /** - * Thrown to indicate that an actor referenced by an endpoint URI cannot be + * Thrown to indicate that an actor referenced by an endpoint URI cannot be * found in the ActorRegistry. * * @author Martin Krasser diff --git a/akka-camel/src/main/scala/service/CamelService.scala b/akka-camel/src/main/scala/service/CamelService.scala index 0f61f2c0f3..86b4f2dc23 100644 --- a/akka-camel/src/main/scala/service/CamelService.scala +++ b/akka-camel/src/main/scala/service/CamelService.scala @@ -17,6 +17,7 @@ import se.scalablesolutions.akka.util.{Bootable, Logging} */ trait CamelService extends Bootable with Logging { + import se.scalablesolutions.akka.actor.Actor.Sender.Self import CamelContextManager._ private[camel] val consumerPublisher = new ConsumerPublisher @@ -31,8 +32,8 @@ trait CamelService extends Bootable with Logging { super.onLoad // Only init and start if not already done by application - if (!initialized) init() - if (!started) start() + if (!initialized) init + if (!started) start // Camel should cache input streams context.setStreamCaching(true) @@ -44,7 +45,7 @@ trait CamelService extends Bootable with Logging { ActorRegistry.addRegistrationListener(publishRequestor.start) // publish already registered consumer actors - for (publish <- Publish.forConsumers(ActorRegistry.actors)) consumerPublisher.!(publish)(None) + for (publish <- Publish.forConsumers(ActorRegistry.actors)) consumerPublisher ! publish } /** @@ -54,7 +55,7 @@ trait CamelService extends Bootable with Logging { ActorRegistry.removeRegistrationListener(publishRequestor) publishRequestor.stop consumerPublisher.stop - stop() + stop super.onUnload } diff --git a/akka-camel/src/main/scala/service/ConsumerPublisher.scala b/akka-camel/src/main/scala/service/ConsumerPublisher.scala index 4e5adea0b8..dee30882f1 100644 --- a/akka-camel/src/main/scala/service/ConsumerPublisher.scala +++ b/akka-camel/src/main/scala/service/ConsumerPublisher.scala @@ -35,9 +35,7 @@ class ConsumerPublisher extends Actor with Logging { * Sets the number of expected Publish messages received by this actor. Used for testing * only. */ - private[camel] def expectPublishCount(count: Int) { - latch = new CountDownLatch(count) - } + private[camel] def expectPublishCount(count: Int): Unit = latch = new CountDownLatch(count) /** * Waits for the number of expected Publish messages to arrive. Used for testing only. @@ -109,37 +107,29 @@ case class Publish(endpointUri: String, id: String, uuid: Boolean) * @author Martin Krasser */ object Publish { + /** * Creates a list of Publish request messages for all consumer actors in the actors * list. */ - def forConsumers(actors: List[Actor]): List[Publish] = { + def forConsumers(actors: List[Actor]): List[Publish] = for (actor <- actors; pub = forConsumer(actor); if pub.isDefined) yield pub.get - } /** * Creates a Publish request message if actor is a consumer actor. */ - def forConsumer(actor: Actor): Option[Publish] = { + def forConsumer(actor: Actor): Option[Publish] = forConsumeAnnotated(actor) orElse forConsumerType(actor) - } private def forConsumeAnnotated(actor: Actor): Option[Publish] = { val annotation = actor.getClass.getAnnotation(classOf[consume]) - if (annotation eq null) - None - else if (actor._remoteAddress.isDefined) - None // do not publish proxies - else - Some(Publish(annotation.value, actor.getId, false)) + if (annotation eq null) None + else if (actor._remoteAddress.isDefined) None // do not publish proxies + else Some(Publish(annotation.value, actor.getId, false)) } - private def forConsumerType(actor: Actor): Option[Publish] = { - if (!actor.isInstanceOf[Consumer]) - None - else if (actor._remoteAddress.isDefined) - None - else - Some(Publish(actor.asInstanceOf[Consumer].endpointUri, actor.uuid, true)) - } + private def forConsumerType(actor: Actor): Option[Publish] = + if (!actor.isInstanceOf[Consumer]) None + else if (actor._remoteAddress.isDefined) None + else Some(Publish(actor.asInstanceOf[Consumer].endpointUri, actor.uuid, true)) } diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 5f48f1cc1e..6ce822b7ed 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -218,7 +218,6 @@ trait Actor extends TransactionManagement { private[akka] var _replyToAddress: Option[InetSocketAddress] = None private[akka] val _mailbox: Queue[MessageInvocation] = new ConcurrentLinkedQueue[MessageInvocation] - // ==================================== // protected fields // ==================================== From 2f4d45aec0ff214e6a065098e8fe54f54ffb3a98 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 16 Mar 2010 23:21:13 +0100 Subject: [PATCH 65/81] Removed dead code --- akka-core/src/main/scala/actor/Actor.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 5f48f1cc1e..b0991936fb 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -501,8 +501,6 @@ trait Actor extends TransactionManagement { def !![T](message: Any, timeout: Long): Option[T] = { if (_isKilled) throw new ActorKilledException("Actor [" + toString + "] has been killed, can't respond to messages") if (_isRunning) { - val from = if (sender != null && sender.isInstanceOf[Actor]) Some(sender.asInstanceOf[Actor]) - else None val future = postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, None) val isActiveObject = message.isInstanceOf[Invocation] if (isActiveObject && message.asInstanceOf[Invocation].isVoid) future.completeWithResult(None) From db092f1a47e0e0c052214acd8546af79f1daabd5 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 16 Mar 2010 23:24:21 +0100 Subject: [PATCH 66/81] Added the run_akka.sh script --- scripts/run_akka.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 scripts/run_akka.sh diff --git a/scripts/run_akka.sh b/scripts/run_akka.sh new file mode 100755 index 0000000000..c07397adeb --- /dev/null +++ b/scripts/run_akka.sh @@ -0,0 +1,16 @@ +#!/bin/bash +cd $AKKA_HOME +VERSION=akka_2.7.7-0.7-SNAPSHOT +TARGET_DIR=dist/$1 +shift 1 +VMARGS=$@ + +if [ -d $TARGET_DIR ]; then + cd $TARGET_DIR +else + unzip dist/${VERSION}.zip -d $TARGET_DIR + cd $TARGET_DIR +fi + +export AKKA_HOME=`pwd` +java -jar ${VMARGS} ${VERSION}.jar \ No newline at end of file From f0e53bc049a584e7f6c01a6b45968e40064e4d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 14:31:50 +0100 Subject: [PATCH 67/81] moved akka.annotation._ to akka.actor.annotation._ to be merged in with akka-core OSGi bundle --- akka-camel/src/main/scala/service/ConsumerPublisher.scala | 2 +- akka-camel/src/test/scala/service/CamelServiceTest.scala | 2 +- akka-core/src/main/scala/actor/ActiveObject.scala | 2 +- .../src/test/java/se/scalablesolutions/akka/api/Bar.java | 2 +- .../src/test/java/se/scalablesolutions/akka/api/Foo.java | 2 +- .../java/se/scalablesolutions/akka/api/InMemStateful.java | 8 ++++---- .../scalablesolutions/akka/api/InMemStatefulNested.java | 4 ++-- .../se/scalablesolutions/akka/api/PersistentClasher.java | 2 +- .../se/scalablesolutions/akka/api/PersistentStateful.java | 4 ++-- .../akka/api/PersistentStatefulNested.java | 4 ++-- .../akka-sample-camel/src/main/scala/Actors.scala | 2 +- .../main/java/sample/java/PersistentSimpleService.java | 6 +++--- .../src/main/java/sample/java/SimpleService.java | 6 +++--- .../scalablesolutions/akka/annotation/configuration.java | 2 +- .../se/scalablesolutions/akka/annotation/consume.java | 2 +- .../se/scalablesolutions/akka/annotation/immutable.java | 2 +- .../akka/annotation/inittransactionalstate.java | 2 +- .../java/se/scalablesolutions/akka/annotation/oneway.java | 2 +- .../se/scalablesolutions/akka/annotation/postrestart.java | 2 +- .../se/scalablesolutions/akka/annotation/prerestart.java | 2 +- .../java/se/scalablesolutions/akka/annotation/state.java | 2 +- .../akka/annotation/transactionrequired.java | 2 +- 22 files changed, 32 insertions(+), 32 deletions(-) diff --git a/akka-camel/src/main/scala/service/ConsumerPublisher.scala b/akka-camel/src/main/scala/service/ConsumerPublisher.scala index dee30882f1..a6509e2694 100644 --- a/akka-camel/src/main/scala/service/ConsumerPublisher.scala +++ b/akka-camel/src/main/scala/service/ConsumerPublisher.scala @@ -9,7 +9,7 @@ import java.util.concurrent.CountDownLatch import org.apache.camel.builder.RouteBuilder import se.scalablesolutions.akka.actor.{ActorUnregistered, ActorRegistered, Actor} -import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.actor.annotation.consume import se.scalablesolutions.akka.camel.{Consumer, CamelContextManager} import se.scalablesolutions.akka.util.Logging diff --git a/akka-camel/src/test/scala/service/CamelServiceTest.scala b/akka-camel/src/test/scala/service/CamelServiceTest.scala index 8fafea4687..a3b0f5c913 100644 --- a/akka-camel/src/test/scala/service/CamelServiceTest.scala +++ b/akka-camel/src/test/scala/service/CamelServiceTest.scala @@ -5,7 +5,7 @@ import org.junit.Assert._ import org.scalatest.junit.JUnitSuite import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.actor.annotation.consume import se.scalablesolutions.akka.camel.{CamelContextManager, Consumer, Message} import org.junit.{Ignore, Before, After, Test} diff --git a/akka-core/src/main/scala/actor/ActiveObject.scala b/akka-core/src/main/scala/actor/ActiveObject.scala index d88f0e861b..9b5a6b409a 100644 --- a/akka-core/src/main/scala/actor/ActiveObject.scala +++ b/akka-core/src/main/scala/actor/ActiveObject.scala @@ -19,7 +19,7 @@ import java.net.InetSocketAddress import java.lang.reflect.{InvocationTargetException, Method} object Annotations { - import se.scalablesolutions.akka.annotation._ + import se.scalablesolutions.akka.actor.annotation._ val oneway = classOf[oneway] val transactionrequired = classOf[transactionrequired] val prerestart = classOf[prerestart] diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Bar.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Bar.java index 9a3ff80aca..3d85d89a17 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Bar.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Bar.java @@ -1,6 +1,6 @@ package se.scalablesolutions.akka.api; -import se.scalablesolutions.akka.annotation.oneway; +import se.scalablesolutions.akka.actor.annotation.oneway; public interface Bar { @oneway diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Foo.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Foo.java index bb9cfd83d4..962f0b9424 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Foo.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/Foo.java @@ -1,7 +1,7 @@ package se.scalablesolutions.akka.api; import com.google.inject.Inject; -import se.scalablesolutions.akka.annotation.oneway; +import se.scalablesolutions.akka.actor.annotation.oneway; public class Foo extends se.scalablesolutions.akka.serialization.Serializable.JavaJSON { @Inject diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java index 8bc60ad922..afe2f2e232 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java @@ -1,9 +1,9 @@ package se.scalablesolutions.akka.api; -import se.scalablesolutions.akka.annotation.transactionrequired; -import se.scalablesolutions.akka.annotation.prerestart; -import se.scalablesolutions.akka.annotation.postrestart; -import se.scalablesolutions.akka.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.prerestart; +import se.scalablesolutions.akka.actor.annotation.postrestart; +import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; import se.scalablesolutions.akka.stm.*; @transactionrequired diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java index ca6b345766..932dc2c162 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java @@ -1,7 +1,7 @@ package se.scalablesolutions.akka.api; -import se.scalablesolutions.akka.annotation.transactionrequired; -import se.scalablesolutions.akka.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; import se.scalablesolutions.akka.stm.*; @transactionrequired diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java index caa755ab83..d5c1bdf00c 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java @@ -2,7 +2,7 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.persistence.common.*; import se.scalablesolutions.akka.persistence.cassandra.*; -import se.scalablesolutions.akka.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; public class PersistentClasher { private PersistentMap state; diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java index 176c551e13..6a8d3353b7 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java @@ -1,7 +1,7 @@ package se.scalablesolutions.akka.api; -import se.scalablesolutions.akka.annotation.inittransactionalstate; -import se.scalablesolutions.akka.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; import se.scalablesolutions.akka.persistence.common.*; import se.scalablesolutions.akka.persistence.cassandra.*; diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java index a919279c0a..bd931ef108 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java @@ -1,7 +1,7 @@ package se.scalablesolutions.akka.api; -import se.scalablesolutions.akka.annotation.inittransactionalstate; -import se.scalablesolutions.akka.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; import se.scalablesolutions.akka.persistence.common.*; import se.scalablesolutions.akka.persistence.cassandra.*; diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala index 11367dedb4..c82b29afc9 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -1,7 +1,7 @@ package sample.camel import se.scalablesolutions.akka.actor.{Actor, RemoteActor} -import se.scalablesolutions.akka.annotation.consume +import se.scalablesolutions.akka.actor.annotation.consume import se.scalablesolutions.akka.camel.{Producer, Message, Consumer} import se.scalablesolutions.akka.util.Logging diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/java/PersistentSimpleService.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/java/PersistentSimpleService.java index 2597e45d95..221b5613b8 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/java/PersistentSimpleService.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/java/PersistentSimpleService.java @@ -8,9 +8,9 @@ import javax.ws.rs.Path; import javax.ws.rs.GET; import javax.ws.rs.Produces; -import se.scalablesolutions.akka.annotation.transactionrequired; -import se.scalablesolutions.akka.annotation.prerestart; -import se.scalablesolutions.akka.annotation.postrestart; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.prerestart; +import se.scalablesolutions.akka.actor.annotation.postrestart; import se.scalablesolutions.akka.persistence.common.PersistentMap; import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage; diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/java/SimpleService.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/java/SimpleService.java index 0d86b2d37b..b10bcdaea4 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/java/SimpleService.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/java/SimpleService.java @@ -8,9 +8,9 @@ import javax.ws.rs.Path; import javax.ws.rs.GET; import javax.ws.rs.Produces; -import se.scalablesolutions.akka.annotation.transactionrequired; -import se.scalablesolutions.akka.annotation.prerestart; -import se.scalablesolutions.akka.annotation.postrestart; +import se.scalablesolutions.akka.actor.annotation.transactionrequired; +import se.scalablesolutions.akka.actor.annotation.prerestart; +import se.scalablesolutions.akka.actor.annotation.postrestart; import se.scalablesolutions.akka.stm.TransactionalState; import se.scalablesolutions.akka.stm.TransactionalMap; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/configuration.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/configuration.java index b0139ac6f0..9c5375398b 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/configuration.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/configuration.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java index 3f8ab9455a..17ac05bf17 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/consume.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; + package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/immutable.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/immutable.java index 9dd2d17322..84dbbf4636 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/immutable.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/immutable.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java index 50e42546ad..35c5f05afe 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/inittransactionalstate.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java index fa7084bb07..45440b5613 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java index d003a38df8..5eed474832 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/postrestart.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java index e65f38cad6..94f9a01405 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/prerestart.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/state.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/state.java index 1e627dde8e..509d129c1b 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/state.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/state.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; diff --git a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java index c45482c467..c41a09ee46 100644 --- a/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java +++ b/akka-util-java/src/main/java/se/scalablesolutions/akka/annotation/transactionrequired.java @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.annotation; +package se.scalablesolutions.akka.actor.annotation; import java.lang.annotation.*; From b9451a5991ee8b66ba17c8b27021e89e8cc93209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 15:10:22 +0100 Subject: [PATCH 68/81] Made "sbt publish" publish artifacts to local Maven repo --- project/build/AkkaProject.scala | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 326daab975..158f193494 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -60,6 +60,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // repositories + val embeddedrepo = "embedded repo" at "file://" + akkaHome + "/embedded-repo" val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" val databinder = "DataBinder" at "http://databinder.net/repo" val configgy = "Configgy" at "http://www.lag.net/repo" @@ -67,7 +68,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val codehaus_snapshots = "Codehaus Snapshots" at "http://snapshots.repository.codehaus.org" val jboss = "jBoss" at "http://repository.jboss.org/maven2" val guiceyfruit = "GuiceyFruit" at "http://guiceyfruit.googlecode.com/svn/repo/releases/" - val embeddedrepo = "embedded repo" at "http://guice-maven.googlecode.com/svn/trunk" val google = "google" at "http://google-maven-repository.googlecode.com/svn/repository" val m2 = "m2" at "http://download.java.net/maven/2" @@ -126,18 +126,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // publishing - Credentials(Path.userHome / ".akka_publish_credentials", log) override def managedStyle = ManagedStyle.Maven + val publishTo = Resolver.file("maven-local", new java.io.File((Path.userHome / ".m2" / "repository").toString)) - val publishTo = "Scalable Solutions Maven Repository" at "~/tmp/akka" -// val publishTo = "Scalable Solutions Maven Repository" at "http://scalablesolutions.se/akka/repository/" + // Credentials(Path.userHome / ".akka_publish_credentials", log) val sourceArtifact = Artifact(artifactID, "src", "jar", Some("sources"), Nil, None) // val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None) override def packageDocsJar = defaultJarPath("-javadoc.jar") override def packageSrcJar= defaultJarPath("-sources.jar") override def packageToPublishActions = super.packageToPublishActions ++ Seq(packageDocs, packageSrc) - + override def pomExtra = 2009 http://akkasource.org @@ -153,7 +152,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { - // ------------------------------------------------------------ + // ------------------------------------------------------------ // subprojects class AkkaCoreProject(info: ProjectInfo) extends DefaultProject(info) { val netty = "org.jboss.netty" % "netty" % "3.2.0.BETA1" % "compile" From 6acd56a53d739df934ebdfd01ae78863b5591fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 15:56:12 +0100 Subject: [PATCH 69/81] Changed Chat sample to use server-managed remote actors + changed the how-to-run-sample doc. --- .../src/main/scala/ChatServer.scala | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index ff79df252b..dcf8f74d6b 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -1,30 +1,38 @@ -/**ChatStorage +/** * Copyright (C) 2009-2010 Scalable Solutions AB . */ package se.scalablesolutions.akka.sample.chat -import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor, RemoteActor} -import se.scalablesolutions.akka.stm.Transaction._ -import se.scalablesolutions.akka.persistence.common.PersistentVector -import se.scalablesolutions.akka.persistence.redis.RedisStorage -import se.scalablesolutions.akka.remote.RemoteServer -import se.scalablesolutions.akka.util.Logging -import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.config.OneForOneStrategy - import scala.collection.mutable.HashMap +import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor, RemoteActor} +import se.scalablesolutions.akka.remote.{RemoteNode, RemoteClient} +import se.scalablesolutions.akka.persistence.common.PersistentVector +import se.scalablesolutions.akka.persistence.redis.RedisStorage +import se.scalablesolutions.akka.stm.Transaction._ +import se.scalablesolutions.akka.config.ScalaConfig._ +import se.scalablesolutions.akka.config.OneForOneStrategy +import se.scalablesolutions.akka.util.Logging + /****************************************************************************** - To run the sample: - 1. Run 'mvn install' (builds and deploys jar to AKKA_HOME/deploy) - 2. In another shell run 'java -jar ./dist/akka-0.6.jar' to start up Akka microkernel - 3. In the first shell run 'mvn scala:console -o' - 4. In the REPL you get execute: + To run the sample + + 1. Install the Redis network storage. Download it from [http://code.google.com/p/redis/]. + 2. Open up a shell and start up an instance of Redis. + 3. Fire up two shells. For each of them: + - Step down into to the root of the Akka distribution. + - Set 'export AKKA_HOME=. + - Run 'sbt console' to start up a REPL (interpreter). + 4. In the first REPL you get execute: - scala> import se.scalablesolutions.akka.sample.chat._ - - scala> Runner.run - 5. See the chat simulation run - 6. Run it again to see full speed after first initialization + - scala> ChatService.start + 5. In the first REPL you get execute: + - scala> import se.scalablesolutions.akka.sample.chat._ + - scala> Runner.run + 6. See the chat simulation run. + 7. Run it again to see full speed after first initialization. + ******************************************************************************/ /** @@ -42,10 +50,12 @@ case class ChatMessage(from: String, message: String) extends Event */ class ChatClient(val name: String) { import Actor.Sender.Self - def login = ChatService ! Login(name) - def logout = ChatService ! Logout(name) - def post(message: String) = ChatService ! ChatMessage(name, name + ": " + message) - def chatLog: ChatLog = (ChatService !! GetChatLog(name)).getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) + val chat = RemoteClient.actorFor("chat:service", "localhost", 9999) + + def login = chat ! Login(name) + def logout = chat ! Logout(name) + def post(message: String) = chat ! ChatMessage(name, name + ": " + message) + def chatLog: ChatLog = (chat !! GetChatLog(name)).getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) } /** @@ -182,16 +192,19 @@ object ChatService extends ChatServer with SessionManagement with ChatManagement with - RedisChatStorageFactory + RedisChatStorageFactory { + override def start: Actor = { + super.start + RemoteNode.start("localhost", 9999) + RemoteNode.register("chat:service", this) + this + } +} /** * Test runner emulating a chat session. */ object Runner { - // create a handle to the remote ChatService - ChatService.makeRemote("localhost", 9999) - ChatService.start - def run = { val client = new ChatClient("jonas") @@ -205,4 +218,4 @@ object Runner { client.logout } -} +} \ No newline at end of file From cfbc5854852194cb8fc62ca16a4c34e86fd9b4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 08:17:18 -0700 Subject: [PATCH 70/81] Updated README with new running procedure --- akka-samples/akka-sample-chat/README | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/akka-samples/akka-sample-chat/README b/akka-samples/akka-sample-chat/README index d2049cd7c0..799da70c24 100644 --- a/akka-samples/akka-sample-chat/README +++ b/akka-samples/akka-sample-chat/README @@ -10,17 +10,20 @@ For details on how to set up Redis server have a look at http://code.google.com/ Then to run the sample: -1. Set ‘AKKA_HOME’ environment variable to the root of the Akka distribution. -2. Open up a shell and step into the Akka distribution root folder. -3. Build Akka by invoking ‘mvn install -Dmaven.test.skip=true’. This will also build the sample application and deploy it to the ‘$AKKA_HOME/deploy’ directory. -4. Run the microkernel - export AKKA_HOME=... - cd $AKKA_HOME - java -jar ./dist/akka-0.6.jar -5. Now start up a new shell and go down into the ‘./akka-samples/akka-sample-chat’ directory. -6. Invoke ‘mvn scala:console -o’. This will give you a Scala REPL (interpreter) with the chat application and all its dependency JARs on the classpath. -7. Simply paste in the whole code block with the ‘Runner’ object above and invoke ‘Runner.run’. This runs a simulated client session that will connect to the running server in the microkernel. -8. Invoke ‘Runner.run’ again and again… +1. Install the Redis network storage. Download it from [http://code.google.com/p/redis/]. +2. Open up a shell and start up an instance of Redis. +3. Fire up two shells. For each of them: + - Step down into to the root of the Akka distribution. + - Set 'export AKKA_HOME=. + - Run 'sbt console' to start up a REPL (interpreter). +4. In the first REPL you get execute: + - scala> import se.scalablesolutions.akka.sample.chat._ + - scala> ChatService.start +5. In the first REPL you get execute: + - scala> import se.scalablesolutions.akka.sample.chat._ + - scala> Runner.run +6. See the chat simulation run. +7. Run it again to see full speed after first initialization. Now you could test client reconnect by killing the running microkernel and start it up again. See the client reconnect take place in the REPL shell. From bf9bbd0dc133bd1bef5bd1a5369ff7f5b072f1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 08:19:56 -0700 Subject: [PATCH 71/81] Updated how to run the sample docs. --- .../src/main/scala/ChatServer.scala | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index dcf8f74d6b..31d4e9d49f 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -16,22 +16,32 @@ import se.scalablesolutions.akka.config.OneForOneStrategy import se.scalablesolutions.akka.util.Logging /****************************************************************************** - To run the sample +Akka Chat Client/Server Sample Application - 1. Install the Redis network storage. Download it from [http://code.google.com/p/redis/]. - 2. Open up a shell and start up an instance of Redis. - 3. Fire up two shells. For each of them: - - Step down into to the root of the Akka distribution. - - Set 'export AKKA_HOME=. - - Run 'sbt console' to start up a REPL (interpreter). - 4. In the first REPL you get execute: +First we need to download, build and start up Redis: + +1. Download Redis from http://code.google.com/p/redis/downloads/list. +2. Step into the distribution. +3. Build: ‘make install’. +4. Run: ‘./redis-server’. +For details on how to set up Redis server have a look at http://code.google.com/p/redis/wiki/QuickStart. + +Then to run the sample: + +1. Fire up two shells. For each of them: + - Step down into to the root of the Akka distribution. + - Set 'export AKKA_HOME=. + - Run 'sbt console' to start up a REPL (interpreter). +2. In the first REPL you get execute: + - scala> import se.scalablesolutions.akka.sample.chat._ + - scala> ChatService.start +3. In the first REPL you get execute: - scala> import se.scalablesolutions.akka.sample.chat._ - - scala> ChatService.start - 5. In the first REPL you get execute: - - scala> import se.scalablesolutions.akka.sample.chat._ - - scala> Runner.run - 6. See the chat simulation run. - 7. Run it again to see full speed after first initialization. + - scala> Runner.run +4. See the chat simulation run. +5. Run it again to see full speed after first initialization. + +That’s it. Have fun. ******************************************************************************/ From ed116712a39380a319f7ced1a65cb921c1f26f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 08:20:46 -0700 Subject: [PATCH 72/81] Fixed typo in docs. --- akka-samples/akka-sample-chat/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-samples/akka-sample-chat/README b/akka-samples/akka-sample-chat/README index 799da70c24..88720d8c55 100644 --- a/akka-samples/akka-sample-chat/README +++ b/akka-samples/akka-sample-chat/README @@ -25,7 +25,7 @@ Then to run the sample: 6. See the chat simulation run. 7. Run it again to see full speed after first initialization. -Now you could test client reconnect by killing the running microkernel and start it up again. See the client reconnect take place in the REPL shell. +Now you could test client reconnect by killing the console running the ChatService and start it up again. See the client reconnect take place in the REPL shell. That’s it. Have fun. From eb4e6242e3fdc955945288fc0f1d9061507a7b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 17:43:49 +0100 Subject: [PATCH 73/81] Created an alias to TransactionalRef; Ref --- .../main/scala/stm/TransactionalState.scala | 20 +++++++++++++++++++ .../src/main/scala/ChatServer.scala | 7 ++++--- project/build/AkkaProject.scala | 4 ++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/akka-core/src/main/scala/stm/TransactionalState.scala b/akka-core/src/main/scala/stm/TransactionalState.scala index c74fde4ab0..4f35f1199e 100644 --- a/akka-core/src/main/scala/stm/TransactionalState.scala +++ b/akka-core/src/main/scala/stm/TransactionalState.scala @@ -53,6 +53,17 @@ trait Committable { } /** + * Alias to TransactionalRef. + * + * @author Jonas Bonér + */ +object Ref { + def apply[T]() = new Ref[T] +} + +/** + * Alias to Ref. + * * @author Jonas Bonér */ object TransactionalRef { @@ -65,8 +76,17 @@ object TransactionalRef { def apply[T]() = new TransactionalRef[T] } +/** + * Implements a transactional managed reference. + * Alias to TransactionalRef. + * + * @author Jonas Bonér + */ +class Ref[T] extends TransactionalRef[T] + /** * Implements a transactional managed reference. + * Alias to Ref. * * @author Jonas Bonér */ diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index dcf8f74d6b..9b85972958 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -87,8 +87,9 @@ trait ChatStorage extends Actor */ class RedisChatStorage extends ChatStorage { lifeCycle = Some(LifeCycle(Permanent)) - - private var chatLog = atomic { RedisStorage.getVector("akka.chat.log") } + val CHAT_LOG = "akka.chat.log" + + private var chatLog = atomic { RedisStorage.getVector(CHAT_LOG) } log.info("Redis-based chat storage is starting up...") @@ -106,7 +107,7 @@ class RedisChatStorage extends ChatStorage { reply(ChatLog(messageList)) } - override def postRestart(reason: Throwable) = chatLog = RedisStorage.getVector("akka.chat.log") + override def postRestart(reason: Throwable) = chatLog = RedisStorage.getVector(CHAT_LOG) } /** diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 158f193494..cd46dbea5c 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -127,11 +127,11 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // publishing override def managedStyle = ManagedStyle.Maven - val publishTo = Resolver.file("maven-local", new java.io.File((Path.userHome / ".m2" / "repository").toString)) + val publishTo = Resolver.file("maven-local", Path.userHome / ".m2" / "repository" asFile) // Credentials(Path.userHome / ".akka_publish_credentials", log) val sourceArtifact = Artifact(artifactID, "src", "jar", Some("sources"), Nil, None) -// val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None) + //val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None) override def packageDocsJar = defaultJarPath("-javadoc.jar") override def packageSrcJar= defaultJarPath("-sources.jar") From 592e4d3d1e47625937f99303edb142836a38e09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 17:48:46 +0100 Subject: [PATCH 74/81] reformatted patterns code --- akka-patterns/src/main/scala/Patterns.scala | 33 ++++++++------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/akka-patterns/src/main/scala/Patterns.scala b/akka-patterns/src/main/scala/Patterns.scala index 02f2686ba7..3b7982148e 100644 --- a/akka-patterns/src/main/scala/Patterns.scala +++ b/akka-patterns/src/main/scala/Patterns.scala @@ -18,19 +18,17 @@ object Patterns { /** * Interceptor is a filter(x,y) where x.isDefinedAt is considered to be always true */ - def intercept[A, B](interceptor: (A) => Unit, interceptee: PF[A, B]): PF[A, B] = filter( - {case a if a.isInstanceOf[A] => interceptor(a)}, - interceptee - ) + def intercept[A, B](interceptor: (A) => Unit, interceptee: PF[A, B]): PF[A, B] = + filter({case a if a.isInstanceOf[A] => interceptor(a)}, interceptee) //FIXME 2.8, use default params with CyclicIterator def loadBalancerActor(actors: => InfiniteIterator[Actor]): Actor = new Actor with LoadBalancer { val seq = actors } - def dispatcherActor(routing: PF[Any, Actor], msgTransformer: (Any) => Any): Actor = new Actor with Dispatcher { + def dispatcherActor(routing: PF[Any, Actor], msgTransformer: (Any) => Any): Actor = + new Actor with Dispatcher { override def transform(msg: Any) = msgTransformer(msg) - def routes = routing } @@ -38,36 +36,29 @@ object Patterns { def routes = routing } - def loggerActor(actorToLog: Actor, logger: (Any) => Unit): Actor = dispatcherActor( - {case _ => actorToLog}, - logger - ) + def loggerActor(actorToLog: Actor, logger: (Any) => Unit): Actor = + dispatcherActor({case _ => actorToLog}, logger) } -trait Dispatcher { - self: Actor => +trait Dispatcher { self: Actor => protected def transform(msg: Any): Any = msg protected def routes: PartialFunction[Any, Actor] protected def dispatch: PartialFunction[Any, Unit] = { - case a if routes.isDefinedAt(a) => { - if (self.sender.isDefined) - routes(a) forward transform(a) - else - routes(a) send transform(a) - } + case a if routes.isDefinedAt(a) => + if (self.sender.isDefined) routes(a) forward transform(a) + else routes(a) send transform(a) } def receive = dispatch } -trait LoadBalancer extends Dispatcher { - self: Actor => +trait LoadBalancer extends Dispatcher { self: Actor => protected def seq: InfiniteIterator[Actor] - protected def routes = {case x if seq.hasNext => seq.next} + protected def routes = { case x if seq.hasNext => seq.next } } trait InfiniteIterator[T] extends Iterator[T] From 3010cd1aa24e85f86923c3584adefb608d1f8687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 17 Mar 2010 21:38:27 +0100 Subject: [PATCH 75/81] Refactored Serializer --- akka-core/src/main/scala/remote/Cluster.scala | 43 +++++++++--------- .../scala/remote/RemoteProtocolBuilder.scala | 14 +++--- .../main/scala/serialization/Serializer.scala | 44 +++++++++++-------- .../src/main/scala/Storage.scala | 2 +- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/akka-core/src/main/scala/remote/Cluster.scala b/akka-core/src/main/scala/remote/Cluster.scala index 7156c999bc..4a1d6012a7 100644 --- a/akka-core/src/main/scala/remote/Cluster.scala +++ b/akka-core/src/main/scala/remote/Cluster.scala @@ -17,41 +17,42 @@ import scala.collection.immutable.{Map, HashMap} * @author Viktor Klang */ trait Cluster { + /** - * Specifies the cluster name - */ + * Specifies the cluster name + */ def name: String /** - * Adds the specified hostname + port as a local node - * This information will be propagated to other nodes in the cluster - * and will be available at the other nodes through lookup and foreach - */ + * Adds the specified hostname + port as a local node + * This information will be propagated to other nodes in the cluster + * and will be available at the other nodes through lookup and foreach + */ def registerLocalNode(hostname: String, port: Int): Unit /** - * Removes the specified hostname + port from the local node - * This information will be propagated to other nodes in the cluster - * and will no longer be available at the other nodes through lookup and foreach - */ + * Removes the specified hostname + port from the local node + * This information will be propagated to other nodes in the cluster + * and will no longer be available at the other nodes through lookup and foreach + */ def deregisterLocalNode(hostname: String, port: Int): Unit /** - * Sends the message to all Actors of the specified type on all other nodes in the cluster - */ + * Sends the message to all Actors of the specified type on all other nodes in the cluster + */ def relayMessage(to: Class[_ <: Actor], msg: AnyRef): Unit /** - * Traverses all known remote addresses avaiable at all other nodes in the cluster - * and applies the given PartialFunction on the first address that it's defined at - * The order of application is undefined and may vary - */ + * Traverses all known remote addresses avaiable at all other nodes in the cluster + * and applies the given PartialFunction on the first address that it's defined at + * The order of application is undefined and may vary + */ def lookup[T](pf: PartialFunction[RemoteAddress, T]): Option[T] /** - * Applies the specified function to all known remote addresses on al other nodes in the cluster - * The order of application is undefined and may vary - */ + * Applies the specified function to all known remote addresses on al other nodes in the cluster + * The order of application is undefined and may vary + */ def foreach(f: (RemoteAddress) => Unit): Unit } @@ -159,7 +160,7 @@ abstract class BasicClusterActor extends ClusterActor { case RegisterLocalNode(s) => { log debug ("RegisterLocalNode: %s", s) - local = Node(local.endpoints + s) + local = Node(s :: local.endpoints) broadcast(Papers(local.endpoints)) } @@ -242,7 +243,7 @@ object Cluster extends Cluster with Logging { "Can't start cluster since the 'akka.remote.cluster.actor' configuration option is not defined") val serializer = Class.forName(config.getString("akka.remote.cluster.serializer", DEFAULT_SERIALIZER_CLASS_NAME)).newInstance.asInstanceOf[Serializer] - serializer setClassLoader loader + serializer.classLoader = Some(loader) try { name map { fqn => diff --git a/akka-core/src/main/scala/remote/RemoteProtocolBuilder.scala b/akka-core/src/main/scala/remote/RemoteProtocolBuilder.scala index 287168140a..bfeec1c34e 100644 --- a/akka-core/src/main/scala/remote/RemoteProtocolBuilder.scala +++ b/akka-core/src/main/scala/remote/RemoteProtocolBuilder.scala @@ -18,19 +18,17 @@ object RemoteProtocolBuilder { private var SERIALIZER_PROTOBUF: Serializer.Protobuf = Serializer.Protobuf - def setClassLoader(classLoader: ClassLoader) = { - SERIALIZER_JAVA = new Serializer.Java - SERIALIZER_JAVA_JSON = new Serializer.JavaJSON - SERIALIZER_SCALA_JSON = new Serializer.ScalaJSON - SERIALIZER_JAVA.setClassLoader(classLoader) - SERIALIZER_JAVA_JSON.setClassLoader(classLoader) - SERIALIZER_SCALA_JSON.setClassLoader(classLoader) + def setClassLoader(cl: ClassLoader) = { + SERIALIZER_JAVA.classLoader = Some(cl) + SERIALIZER_JAVA_JSON.classLoader = Some(cl) + SERIALIZER_SCALA_JSON.classLoader = Some(cl) } def getMessage(request: RemoteRequest): Any = { request.getProtocol match { case SerializationProtocol.SBINARY => - val renderer = Class.forName(new String(request.getMessageManifest.toByteArray)).newInstance.asInstanceOf[SBinary[_ <: AnyRef]] + val renderer = Class.forName( + new String(request.getMessageManifest.toByteArray)).newInstance.asInstanceOf[SBinary[_ <: AnyRef]] renderer.fromBytes(request.getMessage.toByteArray) case SerializationProtocol.SCALA_JSON => val manifest = SERIALIZER_JAVA.in(request.getMessageManifest.toByteArray, None).asInstanceOf[String] diff --git a/akka-core/src/main/scala/serialization/Serializer.scala b/akka-core/src/main/scala/serialization/Serializer.scala index 1cc930a7eb..c878548711 100644 --- a/akka-core/src/main/scala/serialization/Serializer.scala +++ b/akka-core/src/main/scala/serialization/Serializer.scala @@ -18,13 +18,13 @@ import sjson.json.{Serializer => SJSONSerializer} * @author Jonas Bonér */ trait Serializer { - def deepClone(obj: AnyRef): AnyRef - def out(obj: AnyRef): Array[Byte] - def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef - - protected var classLoader: Option[ClassLoader] = None + var classLoader: Option[ClassLoader] = None - def setClassLoader(cl: ClassLoader) = classLoader = Some(cl) + def deepClone(obj: AnyRef): AnyRef + + def out(obj: AnyRef): Array[Byte] + + def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef } // For Java API @@ -55,7 +55,7 @@ object Serializer { * @author Jonas Bonér */ object Java extends Java - class Java extends Serializer { + trait Java extends Serializer { def deepClone(obj: AnyRef): AnyRef = in(out(obj), None) def out(obj: AnyRef): Array[Byte] = { @@ -67,8 +67,9 @@ object Serializer { } def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = { - val in = if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) - else new ObjectInputStream(new ByteArrayInputStream(bytes)) + val in = + if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) + else new ObjectInputStream(new ByteArrayInputStream(bytes)) val obj = in.readObject in.close obj @@ -79,18 +80,21 @@ object Serializer { * @author Jonas Bonér */ object Protobuf extends Protobuf - class Protobuf extends Serializer { + trait Protobuf extends Serializer { def deepClone(obj: AnyRef): AnyRef = in(out(obj), Some(obj.getClass)) def out(obj: AnyRef): Array[Byte] = { - if (!obj.isInstanceOf[Message]) throw new IllegalArgumentException("Can't serialize a non-protobuf message using protobuf [" + obj + "]") + if (!obj.isInstanceOf[Message]) throw new IllegalArgumentException( + "Can't serialize a non-protobuf message using protobuf [" + obj + "]") obj.asInstanceOf[Message].toByteArray } def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = { - if (!clazz.isDefined) throw new IllegalArgumentException("Need a protobuf message class to be able to serialize bytes using protobuf") + if (!clazz.isDefined) throw new IllegalArgumentException( + "Need a protobuf message class to be able to serialize bytes using protobuf") // TODO: should we cache this method lookup? - val message = clazz.get.getDeclaredMethod("getDefaultInstance", EMPTY_CLASS_ARRAY: _*).invoke(null, EMPTY_ANY_REF_ARRAY: _*).asInstanceOf[Message] + val message = clazz.get.getDeclaredMethod( + "getDefaultInstance", EMPTY_CLASS_ARRAY: _*).invoke(null, EMPTY_ANY_REF_ARRAY: _*).asInstanceOf[Message] message.toBuilder().mergeFrom(bytes).build } @@ -104,7 +108,7 @@ object Serializer { * @author Jonas Bonér */ object JavaJSON extends JavaJSON - class JavaJSON extends Serializer { + trait JavaJSON extends Serializer { private val mapper = new ObjectMapper def deepClone(obj: AnyRef): AnyRef = in(out(obj), Some(obj.getClass)) @@ -118,9 +122,11 @@ object Serializer { } def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = { - if (!clazz.isDefined) throw new IllegalArgumentException("Can't deserialize JSON to instance if no class is provided") - val in = if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) - else new ObjectInputStream(new ByteArrayInputStream(bytes)) + if (!clazz.isDefined) throw new IllegalArgumentException( + "Can't deserialize JSON to instance if no class is provided") + val in = + if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) + else new ObjectInputStream(new ByteArrayInputStream(bytes)) val obj = mapper.readValue(in, clazz.get).asInstanceOf[AnyRef] in.close obj @@ -136,7 +142,7 @@ object Serializer { * @author Jonas Bonér */ object ScalaJSON extends ScalaJSON - class ScalaJSON extends Serializer { + trait ScalaJSON extends Serializer { def deepClone(obj: AnyRef): AnyRef = in(out(obj), None) def out(obj: AnyRef): Array[Byte] = SJSONSerializer.SJSON.out(obj) @@ -158,7 +164,7 @@ object Serializer { * @author Jonas Bonér */ object SBinary extends SBinary - class SBinary { + trait SBinary { import sbinary.DefaultProtocol._ def deepClone[T <: AnyRef](obj: T)(implicit w : Writes[T], r : Reads[T]): T = in[T](out[T](obj), None) diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala index ff37dde82e..f21490a1be 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala @@ -302,7 +302,7 @@ trait PersistentRef[T] extends Transactional with Committable { trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] with Transactional with Committable with Logging { - abstract case class QueueOp + sealed trait QueueOp case object ENQ extends QueueOp case object DEQ extends QueueOp From 02e6f5895a832091c41b46002dce94959f808210 Mon Sep 17 00:00:00 2001 From: Debasish Ghosh Date: Thu, 18 Mar 2010 10:51:22 +0530 Subject: [PATCH 76/81] added support for Redis based SortedSet persistence in Akka transactors --- .../src/main/scala/Storage.scala | 122 +++++++++ .../src/main/scala/StorageBackend.scala | 10 +- .../src/main/scala/RedisStorage.scala | 15 ++ .../src/main/scala/RedisStorageBackend.scala | 17 +- .../test/scala/RedisPersistentActorSpec.scala | 2 +- .../scala/RedisPersistentSortedSetSpec.scala | 237 ++++++++++++++++++ .../test/scala/RedisStorageBackendSpec.scala | 12 +- .../redis/redisclient/1.1/redisclient-1.1.jar | Bin 47464 -> 0 bytes .../redis/redisclient/1.1/redisclient-1.1.pom | 8 - project/build/AkkaProject.scala | 2 +- 10 files changed, 404 insertions(+), 21 deletions(-) create mode 100644 akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentSortedSetSpec.scala delete mode 100644 embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.jar delete mode 100755 embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.pom diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala index f21490a1be..0f0eeac912 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala @@ -51,18 +51,24 @@ trait Storage { def newRef: PersistentRef[ElementType] def newQueue: PersistentQueue[ElementType] = // only implemented for redis throw new UnsupportedOperationException + def newSortedSet: PersistentSortedSet[ElementType] = // only implemented for redis + throw new UnsupportedOperationException def getMap(id: String): PersistentMap[ElementType, ElementType] def getVector(id: String): PersistentVector[ElementType] def getRef(id: String): PersistentRef[ElementType] def getQueue(id: String): PersistentQueue[ElementType] = // only implemented for redis throw new UnsupportedOperationException + def getSortedSet(id: String): PersistentSortedSet[ElementType] = // only implemented for redis + throw new UnsupportedOperationException def newMap(id: String): PersistentMap[ElementType, ElementType] def newVector(id: String): PersistentVector[ElementType] def newRef(id: String): PersistentRef[ElementType] def newQueue(id: String): PersistentQueue[ElementType] = // only implemented for redis throw new UnsupportedOperationException + def newSortedSet(id: String): PersistentSortedSet[ElementType] = // only implemented for redis + throw new UnsupportedOperationException } /** @@ -398,3 +404,119 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] transaction.get.get.register(uuid, this) } } + +/** + * Implements a template for a concrete persistent transactional sorted set based storage. + *

    + * Sorting is done based on a zscore. But the computation of zscore has been kept + * outside the abstraction. + *

    + * zscore can be implemented in a variety of ways by the calling class: + *

    + * trait ZScorable {
    + *   def toZScore: Float
    + * }
    + *
    + * class Foo extends ZScorable {
    + *   //.. implemnetation
    + * }
    + * 
    + * Or we can also use views: + *
    + * class Foo {
    + *   //..
    + * }
    + * 
    + * implicit def Foo2Scorable(foo: Foo): ZScorable = new ZScorable {
    + *   def toZScore = {
    + *     //..
    + *   }
    + * }
    + * 
    + * + * and use foo.toZScore to compute the zscore and pass to the APIs. + * + * @author + */ +trait PersistentSortedSet[A] + extends Transactional + with Committable { + + protected val newElems = TransactionalState.newMap[A, Float] + protected val removedElems = TransactionalState.newVector[A] + + val storage: SortedSetStorageBackend[A] + + def commit = { + for ((element, score) <- newElems) storage.zadd(uuid, String.valueOf(score), element) + for (element <- removedElems) storage.zrem(uuid, element) + newElems.clear + removedElems.clear + } + + def +(elem: A, score: Float) = add(elem, score) + + def add(elem: A, score: Float) = { + register + newElems.put(elem, score) + } + + def -(elem: A) = remove(elem) + + def remove(elem: A) = { + register + removedElems.add(elem) + } + + private def inStorage(elem: A): Option[Float] = storage.zscore(uuid, elem) match { + case Some(s) => Some(s.toFloat) + case None => None + } + + def contains(elem: A): Boolean = { + if (newElems contains elem) true + else { + inStorage(elem) match { + case Some(f) => true + case None => false + } + } + } + + def size: Int = newElems.size + storage.zcard(uuid) - removedElems.size + + def zscore(elem: A): Float = { + if (newElems contains elem) newElems.get(elem).get + inStorage(elem) match { + case Some(f) => f + case None => + throw new Predef.NoSuchElementException(elem + " not present") + } + } + + implicit def order(x: (A, Float)) = new Ordered[(A, Float)] { + def compare(that: (A, Float)) = x._2 compare that._2 + } + + def zrange(start: Int, end: Int): List[(A, Float)] = { + // need to operate on the whole range + // get all from the underlying storage + val fromStore = storage.zrangeWithScore(uuid, 0, -1) + val ts = scala.collection.immutable.TreeSet(fromStore: _*) ++ newElems.toList + val l = ts.size + + // -1 means the last element, -2 means the second last + val s = if (start < 0) start + l else start + val e = + if (end < 0) end + l + else if (end >= l) (l - 1) + else end + // slice is open at the end, we need a closed end range + ts.elements.slice(s, e + 1).toList + } + + private def register = { + if (transaction.get.isEmpty) throw new NoTransactionInScopeException + transaction.get.get.register(uuid, this) + } +} diff --git a/akka-persistence/akka-persistence-common/src/main/scala/StorageBackend.scala b/akka-persistence/akka-persistence-common/src/main/scala/StorageBackend.scala index d909f0e4a4..ab0cfaf4d3 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/StorageBackend.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/StorageBackend.scala @@ -69,11 +69,15 @@ trait SortedSetStorageBackend[T] extends StorageBackend { // remove item from sorted set identified by name def zrem(name: String, item: T): Boolean - // cardinality of the set idnetified by name + // cardinality of the set identified by name def zcard(name: String): Int - def zscore(name: String, item: T): String + // zscore of the item from sorted set identified by name + def zscore(name: String, item: T): Option[Float] + // zrange from the sorted set identified by name def zrange(name: String, start: Int, end: Int): List[T] -} + // zrange with score from the sorted set identified by name + def zrangeWithScore(name: String, start: Int, end: Int): List[(T, Float)] +} diff --git a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorage.scala b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorage.scala index 1338a9f8d4..b8aada0572 100644 --- a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorage.scala +++ b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorage.scala @@ -15,16 +15,20 @@ object RedisStorage extends Storage { def newVector: PersistentVector[ElementType] = newVector(UUID.newUuid.toString) def newRef: PersistentRef[ElementType] = newRef(UUID.newUuid.toString) override def newQueue: PersistentQueue[ElementType] = newQueue(UUID.newUuid.toString) + override def newSortedSet: PersistentSortedSet[ElementType] = newSortedSet(UUID.newUuid.toString) def getMap(id: String): PersistentMap[ElementType, ElementType] = newMap(id) def getVector(id: String): PersistentVector[ElementType] = newVector(id) def getRef(id: String): PersistentRef[ElementType] = newRef(id) override def getQueue(id: String): PersistentQueue[ElementType] = newQueue(id) + override def getSortedSet(id: String): PersistentSortedSet[ElementType] = newSortedSet(id) def newMap(id: String): PersistentMap[ElementType, ElementType] = new RedisPersistentMap(id) def newVector(id: String): PersistentVector[ElementType] = new RedisPersistentVector(id) def newRef(id: String): PersistentRef[ElementType] = new RedisPersistentRef(id) override def newQueue(id: String): PersistentQueue[ElementType] = new RedisPersistentQueue(id) + override def newSortedSet(id: String): PersistentSortedSet[ElementType] = + new RedisPersistentSortedSet(id) } /** @@ -63,3 +67,14 @@ class RedisPersistentQueue(id: String) extends PersistentQueue[Array[Byte]] { val uuid = id val storage = RedisStorageBackend } + +/** + * Implements a persistent transactional sorted set based on the Redis + * storage. + * + * @author Debasish Ghosh + */ +class RedisPersistentSortedSet(id: String) extends PersistentSortedSet[Array[Byte]] { + val uuid = id + val storage = RedisStorageBackend +} diff --git a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala index 8bca9c6af6..c48c84fa39 100644 --- a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala +++ b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala @@ -364,11 +364,10 @@ private [akka] object RedisStorageBackend extends } } - def zscore(name: String, item: Array[Byte]): String = withErrorHandling { + def zscore(name: String, item: Array[Byte]): Option[Float] = withErrorHandling { db.zscore(new String(encode(name.getBytes)), new String(item)) match { - case None => - throw new Predef.NoSuchElementException(new String(item) + " not present") - case Some(s) => s + case Some(s) => Some(s.toFloat) + case None => None } } @@ -380,6 +379,16 @@ private [akka] object RedisStorageBackend extends s.map(_.get.getBytes) } } + + def zrangeWithScore(name: String, start: Int, end: Int): List[(Array[Byte], Float)] = withErrorHandling { + db.zrangeWithScore( + new String(encode(name.getBytes)), start.toString, end.toString, RedisClient.ASC) match { + case None => + throw new Predef.NoSuchElementException(name + " not present") + case Some(l) => + l.map{ case (elem, score) => (elem.get.getBytes, score.get.toFloat) } + } + } def flushDB = withErrorHandling(db.flushDb) diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala index 9405789bfd..41ee6fb909 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala @@ -87,7 +87,7 @@ class AccountActor extends Transactor { } @serializable class PersistentFailerActor extends Transactor { - //timeout = 5000 + // timeout = 5000 def receive = { case "Failure" => throw new RuntimeException("expected") diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentSortedSetSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentSortedSetSpec.scala new file mode 100644 index 0000000000..a2abb2cd40 --- /dev/null +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentSortedSetSpec.scala @@ -0,0 +1,237 @@ +package se.scalablesolutions.akka.persistence.redis + +import org.scalatest.Spec +import org.scalatest.Assertions +import org.scalatest.matchers.ShouldMatchers +import org.scalatest.BeforeAndAfterAll +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith + +import se.scalablesolutions.akka.actor.{Actor, Transactor} + +/** + * A persistent actor based on Redis sortedset storage. + *

    + * Needs a running Redis server. + * @author Debasish Ghosh + */ + +trait ZScorable { + def zscore: Float +} + +case class Hacker(name: String, birth: String) extends ZScorable { + def zscore = birth.toFloat +} + +class SetThresholdViolationException extends RuntimeException + +// add hacker to the set +case class ADD(h: Hacker) + +// remove hacker from set +case class REMOVE(h: Hacker) + +// size of the set +case object SIZE + +// zscore of the hacker +case class SCORE(h: Hacker) + +// zrange +case class RANGE(start: Int, end: Int) + +// add and remove subject to the condition that there will be at least 3 hackers +case class MULTI(add: List[Hacker], rem: List[Hacker], failer: Actor) + +class SortedSetActor extends Transactor { + timeout = 100000 + private lazy val hackers = RedisStorage.newSortedSet + + def receive = { + case ADD(h) => + hackers.+(h.name.getBytes, h.zscore) + reply(true) + + case REMOVE(h) => + hackers.-(h.name.getBytes) + reply(true) + + case SIZE => + reply(hackers.size) + + case SCORE(h) => + reply(hackers.zscore(h.name.getBytes)) + + case RANGE(s, e) => + reply(hackers.zrange(s, e)) + + case MULTI(a, r, failer) => + a.foreach{ h: Hacker => + hackers.+(h.name.getBytes, h.zscore) + } + try { + r.foreach{ h => + if (hackers.size <= 3) + throw new SetThresholdViolationException + hackers.-(h.name.getBytes) + } + } catch { + case e: Exception => + failer !! "Failure" + } + reply((a.size, r.size)) + } +} + +import RedisStorageBackend._ + +@RunWith(classOf[JUnitRunner]) +class RedisPersistentSortedSetSpec extends + Spec with + ShouldMatchers with + BeforeAndAfterAll { + + override def beforeAll { + flushDB + println("** destroyed database") + } + + override def afterAll { + flushDB + println("** destroyed database") + } + + val h1 = Hacker("Alan kay", "1940") + val h2 = Hacker("Richard Stallman", "1953") + val h3 = Hacker("Yukihiro Matsumoto", "1965") + val h4 = Hacker("Claude Shannon", "1916") + val h5 = Hacker("Linus Torvalds", "1969") + val h6 = Hacker("Alan Turing", "1912") + + describe("Add and report cardinality of the set") { + val qa = new SortedSetActor + qa.start + + it("should enter 6 hackers") { + qa !! ADD(h1) + qa !! ADD(h2) + qa !! ADD(h3) + qa !! ADD(h4) + qa !! ADD(h5) + qa !! ADD(h6) + (qa !! SIZE).get.asInstanceOf[Int] should equal(6) + } + + it("should fetch correct scores for hackers") { + (qa !! SCORE(h1)).get.asInstanceOf[Float] should equal(1940.0f) + (qa !! SCORE(h5)).get.asInstanceOf[Float] should equal(1969.0f) + (qa !! SCORE(h6)).get.asInstanceOf[Float] should equal(1912.0f) + } + + it("should fetch proper range") { + (qa !! RANGE(0, 4)).get.asInstanceOf[List[_]].size should equal(5) + (qa !! RANGE(0, 6)).get.asInstanceOf[List[_]].size should equal(6) + } + + it("should remove and throw exception for removing non-existent hackers") { + qa !! REMOVE(h2) + (qa !! SIZE).get.asInstanceOf[Int] should equal(5) + qa !! REMOVE(h3) + (qa !! SIZE).get.asInstanceOf[Int] should equal(4) + val h7 = Hacker("Paul Snively", "1952") + try { + qa !! REMOVE(h7) + } + catch { + case e: Predef.NoSuchElementException => + e.getMessage should endWith("not present") + } + } + + it("should change score for entering the same hacker name with diff score") { + (qa !! SIZE).get.asInstanceOf[Int] should equal(4) + + // same name as h6 + val h7 = Hacker("Alan Turing", "1992") + qa !! ADD(h7) + + // size remains same + (qa !! SIZE).get.asInstanceOf[Int] should equal(4) + + // score updated + (qa !! SCORE(h7)).get.asInstanceOf[Float] should equal(1992.0f) + } + } + + describe("Transaction semantics") { + it("should rollback on exception") { + val qa = new SortedSetActor + qa.start + + val failer = new PersistentFailerActor + failer.start + + (qa !! SIZE).get.asInstanceOf[Int] should equal(0) + val add = List(h1, h2, h3, h4) + val rem = List(h2) + (qa !! MULTI(add, rem, failer)).get.asInstanceOf[Tuple2[Int, Int]] should equal((4,1)) + (qa !! SIZE).get.asInstanceOf[Int] should equal(3) + // size == 3 + + // add 2 more + val add1 = List(h5, h6) + + // remove 3 + val rem1 = List(h1, h3, h4) + try { + qa !! MULTI(add1, rem1, failer) + } catch { case e: Exception => {} + } + (qa !! SIZE).get.asInstanceOf[Int] should equal(3) + } + } + + describe("zrange") { + it ("should report proper range") { + val qa = new SortedSetActor + qa.start + qa !! ADD(h1) + qa !! ADD(h2) + qa !! ADD(h3) + qa !! ADD(h4) + qa !! ADD(h5) + qa !! ADD(h6) + (qa !! SIZE).get.asInstanceOf[Int] should equal(6) + val l = (qa !! RANGE(0, 6)).get.asInstanceOf[List[(Array[Byte], Float)]] + l.map { case (e, s) => (new String(e), s) }.head should equal(("Alan Turing", 1912.0f)) + val h7 = Hacker("Alan Turing", "1992") + qa !! ADD(h7) + (qa !! SIZE).get.asInstanceOf[Int] should equal(6) + val m = (qa !! RANGE(0, 6)).get.asInstanceOf[List[(Array[Byte], Float)]] + m.map { case (e, s) => (new String(e), s) }.head should equal(("Claude Shannon", 1916.0f)) + } + + it ("should report proper rge") { + val qa = new SortedSetActor + qa.start + qa !! ADD(h1) + qa !! ADD(h2) + qa !! ADD(h3) + qa !! ADD(h4) + qa !! ADD(h5) + qa !! ADD(h6) + (qa !! SIZE).get.asInstanceOf[Int] should equal(6) + (qa !! RANGE(0, 5)).get.asInstanceOf[List[_]].size should equal(6) + (qa !! RANGE(0, 6)).get.asInstanceOf[List[_]].size should equal(6) + (qa !! RANGE(0, 3)).get.asInstanceOf[List[_]].size should equal(4) + (qa !! RANGE(0, 1)).get.asInstanceOf[List[_]].size should equal(2) + (qa !! RANGE(0, 0)).get.asInstanceOf[List[_]].size should equal(1) + (qa !! RANGE(3, 1)).get.asInstanceOf[List[_]].size should equal(0) + (qa !! RANGE(0, -1)).get.asInstanceOf[List[_]].size should equal(6) + (qa !! RANGE(0, -2)).get.asInstanceOf[List[_]].size should equal(5) + (qa !! RANGE(0, -4)).get.asInstanceOf[List[_]].size should equal(3) + (qa !! RANGE(-4, -1)).get.asInstanceOf[List[_]].size should equal(4) + } + } +} diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisStorageBackendSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisStorageBackendSpec.scala index cfe704c6ba..44081a43c6 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisStorageBackendSpec.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisStorageBackendSpec.scala @@ -191,10 +191,10 @@ class RedisStorageBackendSpec extends zcard("hackers") should equal(6) - zscore("hackers", "alan turing".getBytes) should equal("1912") - zscore("hackers", "richard stallman".getBytes) should equal("1953") - zscore("hackers", "claude shannon".getBytes) should equal("1916") - zscore("hackers", "linus torvalds".getBytes) should equal("1969") + zscore("hackers", "alan turing".getBytes).get should equal(1912.0f) + zscore("hackers", "richard stallman".getBytes).get should equal(1953.0f) + zscore("hackers", "claude shannon".getBytes).get should equal(1916.0f) + zscore("hackers", "linus torvalds".getBytes).get should equal(1969.0f) val s: List[Array[Byte]] = zrange("hackers", 0, 2) s.size should equal(3) @@ -206,6 +206,10 @@ class RedisStorageBackendSpec extends val t: List[Array[Byte]] = zrange("hackers", 0, -1) t.size should equal(6) t.map(new String(_)) should equal(sorted) + + val u: List[(Array[Byte], Float)] = zrangeWithScore("hackers", 0, -1) + u.size should equal(6) + u.map{ case (e, s) => new String(e) } should equal(sorted) } } } diff --git a/embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.jar b/embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.jar deleted file mode 100644 index a269f15f7aaab12b04c9a2ba8fc4c6572b0f017f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47464 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 Iyb8nv0Q<{W6aWAK diff --git a/embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.pom b/embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.pom deleted file mode 100755 index 16dd81402a..0000000000 --- a/embedded-repo/com/redis/redisclient/1.1/redisclient-1.1.pom +++ /dev/null @@ -1,8 +0,0 @@ - - - 4.0.0 - com.redis - redisclient - 1.1 - jar - diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index cd46dbea5c..783a8d010c 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -240,7 +240,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } class AkkaRedisProject(info: ProjectInfo) extends DefaultProject(info) { - val redis = "com.redis" % "redisclient" % "1.1" % "compile" + val redis = "com.redis" % "redisclient" % "1.2-SNAPSHOT" % "compile" override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil lazy val dist = deployTask(info, distPath) dependsOn(`package`) describedAs("Deploying") } From d50bfe9d89971e6303bc2a44aa9cd23a5a481999 Mon Sep 17 00:00:00 2001 From: Debasish Ghosh Date: Thu, 18 Mar 2010 11:00:02 +0530 Subject: [PATCH 77/81] added new jar 1.2-SNAPSHOT for redisclient --- .../1.2-SNAPSHOT/redisclient-1.2-SNAPSHOT.jar | Bin 0 -> 45796 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 embedded-repo/com/redis/redisclient/1.2-SNAPSHOT/redisclient-1.2-SNAPSHOT.jar diff --git a/embedded-repo/com/redis/redisclient/1.2-SNAPSHOT/redisclient-1.2-SNAPSHOT.jar b/embedded-repo/com/redis/redisclient/1.2-SNAPSHOT/redisclient-1.2-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..88815a75d952c051b1282320cc463a5346d7050c GIT binary patch literal 45796 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 Kz+krw!~+2S^+byR literal 0 HcmV?d00001 From c8a7b3c6a91f13a0c761f14e00c298a247a2fa23 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 18 Mar 2010 16:34:08 +0100 Subject: [PATCH 78/81] extension/rewrite of actor component unit and functional tests --- .../component/ActorComponentFeatureTest.scala | 62 +++++++++++++ .../scala/component/ActorComponentTest.scala | 59 +++++-------- .../scala/component/ActorProducerTest.scala | 87 +++++++++++++------ .../src/test/scala/support/TestSupport.scala | 49 +++++++++++ 4 files changed, 194 insertions(+), 63 deletions(-) create mode 100644 akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala create mode 100644 akka-camel/src/test/scala/support/TestSupport.scala diff --git a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala new file mode 100644 index 0000000000..58b2cdb169 --- /dev/null +++ b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala @@ -0,0 +1,62 @@ +package se.scalablesolutions.akka.camel.component + +import org.apache.camel.RuntimeCamelException +import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec} + +import se.scalablesolutions.akka.actor.ActorRegistry +import se.scalablesolutions.akka.camel.CamelContextManager +import se.scalablesolutions.akka.camel.support.{Respond, Countdown, Tester, Retain} + +class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach { + override protected def beforeAll() = { + ActorRegistry.shutdownAll + CamelContextManager.init + CamelContextManager.start + } + + override protected def afterAll() = CamelContextManager.stop + + override protected def afterEach() = ActorRegistry.shutdownAll + + feature("Communicate with an actor from a Camel application using actor endpoint URIs") { + import CamelContextManager.template + + scenario("one-way communication using actor id") { + val actor = new Tester with Retain with Countdown + actor.start + template.sendBody("actor:%s" format actor.getId, "Martin") + assert(actor.waitFor) + assert(actor.body === "Martin") + } + + scenario("one-way communication using actor uuid") { + val actor = new Tester with Retain with Countdown + actor.start + template.sendBody("actor:uuid:%s" format actor.uuid, "Martin") + assert(actor.waitFor) + assert(actor.body === "Martin") + } + + scenario("two-way communication using actor id") { + val actor = new Tester with Respond + actor.start + assert(template.requestBody("actor:%s" format actor.getId, "Martin") === "Hello Martin") + } + + scenario("two-way communication using actor uuid") { + val actor = new Tester with Respond + actor.start + assert(template.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin") + } + + scenario("two-way communication with timeout") { + val actor = new Tester { + timeout = 1 + } + actor.start + intercept[RuntimeCamelException] { + template.requestBody("actor:uuid:%s" format actor.uuid, "Martin") + } + } + } +} \ No newline at end of file diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala index a856b9ac25..3216a9ca2c 100644 --- a/akka-camel/src/test/scala/component/ActorComponentTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala @@ -1,49 +1,36 @@ package se.scalablesolutions.akka.camel.component +import org.apache.camel.impl.DefaultCamelContext import org.junit._ -import org.junit.Assert._ +import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.{CamelContextLifecycle, Message} +class ActorComponentTest extends JUnitSuite { -class ActorComponentTest extends JUnitSuite with CamelContextLifecycle { + val component: ActorComponent = ActorComponentTest.mockComponent - // - // TODO: extend/rewrite unit tests - // These tests currently only ensure proper functioning of basic features. - // - - @Before def setUp = { - init - start + @Test def shouldCreateEndpointWithIdDefined = { + val ep1: ActorEndpoint = component.createEndpoint("actor:abc").asInstanceOf[ActorEndpoint] + val ep2: ActorEndpoint = component.createEndpoint("actor:id:abc").asInstanceOf[ActorEndpoint] + assert(ep1.id === Some("abc")) + assert(ep2.id === Some("abc")) + assert(ep1.uuid === None) + assert(ep2.uuid === None) } - @After def tearDown = { - stop + @Test def shouldCreateEndpointWithUuidDefined = { + val ep: ActorEndpoint = component.createEndpoint("actor:uuid:abc").asInstanceOf[ActorEndpoint] + assert(ep.uuid === Some("abc")) + assert(ep.id === None) } - - @Test def shouldReceiveResponseFromActorReferencedById = { - val actor = new TestActor - actor.start - assertEquals("Hello Martin", template.requestBody("actor:%s" format actor.getId, "Martin")) - assertEquals("Hello Martin", template.requestBody("actor:id:%s" format actor.getId, "Martin")) - actor.stop - } - - @Test def shouldReceiveResponseFromActorReferencedByUuid = { - val actor = new TestActor - actor.start - assertEquals("Hello Martin", template.requestBody("actor:uuid:%s" format actor.uuid, "Martin")) - actor.stop - } - - class TestActor extends Actor { - protected def receive = { - case msg: Message => reply("Hello %s" format msg.body) - } - } - } +object ActorComponentTest { + def mockComponent = { + val component = new ActorComponent + component.setCamelContext(new DefaultCamelContext) + component + } + def mockEndpoint(uri:String) = mockComponent.createEndpoint(uri) +} diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala index 954a4d21cd..ea0bed7017 100644 --- a/akka-camel/src/test/scala/component/ActorProducerTest.scala +++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala @@ -1,42 +1,75 @@ package se.scalablesolutions.akka.camel.component -import org.apache.camel.{CamelContext, ExchangePattern} -import org.apache.camel.impl.{DefaultCamelContext, DefaultExchange} -import org.junit.Assert._ -import org.junit.Test +import ActorComponentTest._ + +import org.apache.camel.ExchangePattern +import org.junit.{After, Test} import org.scalatest.junit.JUnitSuite -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.Message +import se.scalablesolutions.akka.actor.ActorRegistry +import se.scalablesolutions.akka.camel.support.{Countdown, Retain, Tester, Respond} +import org.scalatest.BeforeAndAfterAll +import se.scalablesolutions.akka.camel.{Failure, Message} +import java.util.concurrent.TimeoutException -class ActorProducerTest extends JUnitSuite { +class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll { - // - // TODO: extend/rewrite unit tests - // These tests currently only ensure proper functioning of basic features. - // + @After def tearDown = { + ActorRegistry.shutdownAll + } - val context = new DefaultCamelContext - val endpoint = context.getEndpoint("actor:%s" format classOf[TestActor].getName) - val producer = endpoint.createProducer - - @Test def shouldSendAndReceiveMessageBodyAndHeaders = { - val exchange = new DefaultExchange(null.asInstanceOf[CamelContext], ExchangePattern.InOut) - val actor = new TestActor + @Test def shouldSendMessageToActor = { + val actor = new Tester with Retain with Countdown + val endpoint = mockEndpoint("actor:uuid:%s" format actor.uuid) + val exchange = endpoint.createExchange(ExchangePattern.InOnly) actor.start exchange.getIn.setBody("Martin") exchange.getIn.setHeader("k1", "v1") - producer.process(exchange) - assertEquals("Hello Martin", exchange.getOut.getBody) - assertEquals("v1", exchange.getOut.getHeader("k1")) - assertEquals("v2", exchange.getOut.getHeader("k2")) - actor.stop + endpoint.createProducer.process(exchange) + actor.waitFor + assert(actor.body === "Martin") + assert(actor.headers === Map(Message.MessageExchangeId -> exchange.getExchangeId, "k1" -> "v1")) } - class TestActor extends Actor { - protected def receive = { - case msg: Message => reply(Message("Hello %s" format msg.body, Map("k2" -> "v2") ++ msg.headers)) + @Test def shouldSendMessageToActorAndReturnResponse = { + val actor = new Tester with Respond { + override def response(msg: Message) = Message(super.response(msg), Map("k2" -> "v2")) + } + val endpoint = mockEndpoint("actor:uuid:%s" format actor.uuid) + val exchange = endpoint.createExchange(ExchangePattern.InOut) + actor.start + exchange.getIn.setBody("Martin") + exchange.getIn.setHeader("k1", "v1") + endpoint.createProducer.process(exchange) + assert(exchange.getOut.getBody === "Hello Martin") + assert(exchange.getOut.getHeader("k2") === "v2") + } + + @Test def shouldSendMessageToActorAndReturnFailure = { + val actor = new Tester with Respond { + override def response(msg: Message) = Failure(new Exception("testmsg"), Map("k3" -> "v3")) + } + val endpoint = mockEndpoint("actor:uuid:%s" format actor.uuid) + val exchange = endpoint.createExchange(ExchangePattern.InOut) + actor.start + exchange.getIn.setBody("Martin") + exchange.getIn.setHeader("k1", "v1") + endpoint.createProducer.process(exchange) + assert(exchange.getException.getMessage === "testmsg") + assert(exchange.getOut.getBody === null) + assert(exchange.getOut.getHeader("k3") === null) // headers from failure message are currently ignored + } + + @Test def shouldSendMessageToActorAndTimeout: Unit = { + val actor = new Tester { + timeout = 1 + } + val endpoint = mockEndpoint("actor:uuid:%s" format actor.uuid) + val exchange = endpoint.createExchange(ExchangePattern.InOut) + actor.start + exchange.getIn.setBody("Martin") + intercept[TimeoutException] { + endpoint.createProducer.process(exchange) } } - } diff --git a/akka-camel/src/test/scala/support/TestSupport.scala b/akka-camel/src/test/scala/support/TestSupport.scala new file mode 100644 index 0000000000..f6b7998934 --- /dev/null +++ b/akka-camel/src/test/scala/support/TestSupport.scala @@ -0,0 +1,49 @@ +package se.scalablesolutions.akka.camel.support + +import java.util.concurrent.{TimeUnit, CountDownLatch} + +import se.scalablesolutions.akka.camel.Message +import se.scalablesolutions.akka.actor.Actor + +trait Receive { + def onMessage(msg: Message): Unit +} + +trait Respond extends Receive {self: Actor => + abstract override def onMessage(msg: Message): Unit = { + super.onMessage(msg) + reply(response(msg)) + } + def response(msg: Message): Any = "Hello %s" format msg.body +} + +trait Retain extends Receive { + var body: Any = _ + var headers = Map.empty[String, Any] + abstract override def onMessage(msg: Message): Unit = { + super.onMessage(msg) + body = msg.body + headers = msg.headers + } +} + +trait Countdown extends Receive { + val count = 1 + val duration = 5000 + val latch = new CountDownLatch(count) + + def waitFor = latch.await(duration, TimeUnit.MILLISECONDS) + def countDown = latch.countDown + + abstract override def onMessage(msg: Message) = { + super.onMessage(msg) + countDown + } +} + +class Tester extends Actor with Receive { + def receive = { + case msg: Message => onMessage(msg) + } + def onMessage(msg: Message): Unit = {} +} From 61aa156ff95ac2b3c1318b768f0a5e1f8d6c3e48 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Thu, 18 Mar 2010 21:20:48 +0100 Subject: [PATCH 79/81] Fixed issue with file URL to embedded repository on Windows. --- akka-camel/src/test/scala/component/ActorComponentTest.scala | 1 - akka-camel/src/test/scala/component/ActorProducerTest.scala | 5 +++-- project/build/AkkaProject.scala | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/akka-camel/src/test/scala/component/ActorComponentTest.scala b/akka-camel/src/test/scala/component/ActorComponentTest.scala index 3216a9ca2c..1f7b42bf08 100644 --- a/akka-camel/src/test/scala/component/ActorComponentTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentTest.scala @@ -2,7 +2,6 @@ package se.scalablesolutions.akka.camel.component import org.apache.camel.impl.DefaultCamelContext import org.junit._ -import org.scalatest.BeforeAndAfterAll import org.scalatest.junit.JUnitSuite class ActorComponentTest extends JUnitSuite { diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala index ea0bed7017..afb4a12ef0 100644 --- a/akka-camel/src/test/scala/component/ActorProducerTest.scala +++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala @@ -2,15 +2,16 @@ package se.scalablesolutions.akka.camel.component import ActorComponentTest._ +import java.util.concurrent.TimeoutException + import org.apache.camel.ExchangePattern import org.junit.{After, Test} import org.scalatest.junit.JUnitSuite +import org.scalatest.BeforeAndAfterAll import se.scalablesolutions.akka.actor.ActorRegistry import se.scalablesolutions.akka.camel.support.{Countdown, Retain, Tester, Respond} -import org.scalatest.BeforeAndAfterAll import se.scalablesolutions.akka.camel.{Failure, Message} -import java.util.concurrent.TimeoutException class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll { diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 783a8d010c..4a9c80170a 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -35,6 +35,7 @@ -------------------------------------------------------------------------------*/ import sbt._ +import java.io.File import java.util.jar.Attributes class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { @@ -60,7 +61,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // repositories - val embeddedrepo = "embedded repo" at "file://" + akkaHome + "/embedded-repo" + val embeddedrepo = "embedded repo" at new File(akkaHome, "embedded-repo").toURI.toString val sunjdmk = "sunjdmk" at "http://wp5.e-taxonomy.eu/cdmlib/mavenrepo" val databinder = "DataBinder" at "http://databinder.net/repo" val configgy = "Configgy" at "http://www.lag.net/repo" From 1e6921571084bf9f48627eed683441ee4714f642 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Fri, 19 Mar 2010 08:06:34 +0100 Subject: [PATCH 80/81] Fix for InstantiationException on Kernel startup --- config/akka-reference.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 6c442e3ef1..7e93604521 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -51,9 +51,9 @@ service = on - name = "default" # The name of the cluster - actor = "se.scalablesolutions.akka.cluster.jgroups.JGroupsClusterActor" # FQN of an implementation of ClusterActor - serializer = "se.scalablesolutions.akka.serialization.Serializer$Java" # FQN of the serializer class + name = "default" # The name of the cluster + actor = "se.scalablesolutions.akka.cluster.jgroups.JGroupsClusterActor" # FQN of an implementation of ClusterActor + serializer = "se.scalablesolutions.akka.serialization.Serializer$Java$" # FQN of the serializer class From 8ebb13a5574b546f444060f21ecd05525cb75f47 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Fri, 19 Mar 2010 08:14:00 +0100 Subject: [PATCH 81/81] camel-cometd example disabled --- .../akka-sample-camel/src/main/scala/Boot.scala | 14 ++++++++++---- project/build/AkkaProject.scala | 8 ++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index 42cb367076..481804de64 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -43,16 +43,22 @@ class Boot { // Publish subscribe example - val cometdUri = "cometd://localhost:8111/test/abc?resourceBase=target" - val cometdSubscriber = new Subscriber("cometd-subscriber", cometdUri).start - val cometdPublisher = new Publisher("cometd-publisher", cometdUri).start + // + // Cometd example is disabled because of unresolved sbt/ivy dependency resolution issues. + // If you want to run this example, make sure to replace all jetty-*-6.1.22.jar files + // on the classpath with corresponding jetty-*-6.1.11.jar files. + // + + //val cometdUri = "cometd://localhost:8111/test/abc?resourceBase=target" + //val cometdSubscriber = new Subscriber("cometd-subscriber", cometdUri).start + //val cometdPublisher = new Publisher("cometd-publisher", cometdUri).start val jmsUri = "jms:topic:test" val jmsSubscriber1 = new Subscriber("jms-subscriber-1", jmsUri).start val jmsSubscriber2 = new Subscriber("jms-subscriber-2", jmsUri).start val jmsPublisher = new Publisher("jms-publisher", jmsUri).start - val cometdPublisherBridge = new PublisherBridge("jetty:http://0.0.0.0:8877/camel/pub/cometd", cometdPublisher).start + //val cometdPublisherBridge = new PublisherBridge("jetty:http://0.0.0.0:8877/camel/pub/cometd", cometdPublisher).start val jmsPublisherBridge = new PublisherBridge("jetty:http://0.0.0.0:8877/camel/pub/jms", jmsPublisher).start } diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 4a9c80170a..a891ce1668 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -313,7 +313,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val lift_util = "net.liftweb" % "lift-util" % "1.1-M6" % "compile" val servlet = "javax.servlet" % "servlet-api" % "2.5" % "compile" // testing - val jetty = "org.mortbay.jetty" % "jetty" % "6.1.11" % "test" + val jetty = "org.mortbay.jetty" % "jetty" % "6.1.22" % "test" val junit = "junit" % "junit" % "4.5" % "test" lazy val dist = deployTask(info, deployPath) dependsOn(`package`) describedAs("Deploying") } @@ -328,12 +328,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } class AkkaSampleCamelProject(info: ProjectInfo) extends DefaultProject(info) { - val jetty = "org.mortbay.jetty" % "jetty" % "6.1.11" % "compile" - val jetty_client = "org.mortbay.jetty" % "jetty-client" % "6.1.11" % "compile" - val camel_http = "org.apache.camel" % "camel-http" % "2.2.0" % "compile" - val camel_jetty = "org.apache.camel" % "camel-jetty" % "2.2.0" % "compile" intransitive() + val camel_jetty = "org.apache.camel" % "camel-jetty" % "2.2.0" % "compile" val camel_jms = "org.apache.camel" % "camel-jms" % "2.2.0" % "compile" - val camel_cometd = "org.apache.camel" % "camel-cometd" % "2.2.0" % "compile" val activemq_core = "org.apache.activemq" % "activemq-core" % "5.3.0" % "compile" lazy val dist = deployTask(info, deployPath) dependsOn(`package`) describedAs("Deploying") }