activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From chir...@apache.org
Subject svn commit: r1127340 [1/2] - in /activemq/activemq-apollo/trunk: apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/ apollo-broker/src/test/scala/org/apache/activemq/apollo/bro...
Date Wed, 25 May 2011 00:14:04 GMT
Author: chirino
Date: Wed May 25 00:14:03 2011
New Revision: 1127340

URL: http://svn.apache.org/viewvc?rev=1127340&view=rev
Log:
Better implementation of https://issues.apache.org/jira/browse/APLO-6
- you can now configure the destination parsing options on a per connector basis.
- Converting the util.path stuff to scala..
- Simplifying destination parsing.

Added:
    activemq/activemq-apollo/trunk/apollo-stomp/src/test/resources/apollo-stomp-custom-dest-delimiters.xml
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.scala
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.scala
    activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.scala
    activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapTest.scala
Removed:
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.java
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.java
    activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.java
    activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapTest.java
Modified:
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/Delivery.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/DestinationParser.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/LocalRouter.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/QueueBinding.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/DestinationConfigurationTest.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/perf/BrokerPerfSupport.scala
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DestinationDTO.java
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DurableSubscriptionDestinationDTO.java
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/QueueDestinationDTO.java
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/TopicDestinationDTO.java
    activemq/activemq-apollo/trunk/apollo-dto/src/test/java/org/apache/activemq/apollo/dto/XmlCodecTest.java
    activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/OpenwireProtocolHandler.scala
    activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/command/ActiveMQDestination.java
    activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompFrame.scala
    activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompProtocolHandler.scala
    activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/dto/StompDTO.java
    activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/StompTest.scala
    activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/perf/StompRemoteClients.scala
    activemq/activemq-apollo/trunk/apollo-website/src/documentation/user-manual.md

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/Delivery.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/Delivery.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/Delivery.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/Delivery.scala Wed May 25 00:14:03 2011
@@ -115,11 +115,6 @@ trait Message extends Filterable with Re
   def persistent: Boolean
 
   /**
-   * where the message was sent to.
-   */
-  def destination: Array[DestinationDTO]
-
-  /**
    * The protocol of the message
    */
   def protocol:Protocol

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/DestinationParser.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/DestinationParser.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/DestinationParser.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/DestinationParser.scala Wed May 25 00:14:03 2011
@@ -22,23 +22,21 @@ import Buffer._
 import org.apache.activemq.apollo.util.path.{Path, PathParser}
 import scala.collection.mutable.ListBuffer
 import org.apache.activemq.apollo.dto.{TopicDestinationDTO, QueueDestinationDTO, DestinationDTO}
+import collection.JavaConversions._
+import java.lang.StringBuilder
+import java.util.regex.Pattern
 
 object DestinationParser {
 
-  val default = new DestinationParser
-
-  def encode_path(value:Path) = default.toString(value)
-  def decode_path(value:String) = default.parsePath(ascii(value))
-
-  def encode_destination(value:Array[DestinationDTO]) = default.toString(value)
-  def decode_destination(value:String) = default.parse(ascii(value))
-
-  def create_destination(domain:AsciiBuffer, name:String) = {
-    Array(domain match {
-      case LocalRouter.QUEUE_DOMAIN => new QueueDestinationDTO(name)
-      case LocalRouter.TOPIC_DOMAIN => new TopicDestinationDTO(name)
-      case _ => throw new Exception("Uknown destination domain: "+domain);
-    })
+  val OPENWIRE_PARSER = new DestinationParser();
+  OPENWIRE_PARSER.path_separator = "."
+  OPENWIRE_PARSER.any_child_wildcard = "*"
+  OPENWIRE_PARSER.any_descendant_wildcard = ">"
+
+  def create_destination(domain:String, parts:Array[String]):DestinationDTO = domain match {
+    case LocalRouter.QUEUE_DOMAIN => new QueueDestinationDTO(parts)
+    case LocalRouter.TOPIC_DOMAIN => new TopicDestinationDTO(parts)
+    case _ => throw new Exception("Uknown destination domain: "+domain);
   }
 
 }
@@ -49,44 +47,53 @@ object DestinationParser {
 class DestinationParser extends PathParser {
   import DestinationParser._
 
-  var default_domain: AsciiBuffer = null
-  var queue_prefix: AsciiBuffer = ascii("queue:")
-  var topic_prefix: AsciiBuffer = ascii("topic:")
-  var temp_queue_prefix: AsciiBuffer = ascii("temp-queue:")
-  var temp_topic_prefix: AsciiBuffer = ascii("temp-topic:")
-  var destination_separator: Option[Byte] = Some(','.toByte)
+  var default_domain:String = null
+  var queue_prefix = "queue:"
+  var topic_prefix = "topic:"
+  var temp_queue_prefix = "temp-queue:"
+  var temp_topic_prefix = "temp-topic:"
+  var destination_separator = ","
+
+  def copy(other:DestinationParser) = {
+    super.copy(other)
+    default_domain = other.default_domain
+    queue_prefix = other.queue_prefix
+    topic_prefix = other.topic_prefix
+    temp_queue_prefix = other.temp_queue_prefix
+    temp_topic_prefix = other.temp_topic_prefix
+    destination_separator = other.destination_separator
+    this
+  }
 
-  def toBuffer(value: Array[DestinationDTO]): AsciiBuffer = {
+  def encode_destination(value: Array[DestinationDTO]): String = {
     if (value == null) {
       null
     } else {
-      val baos = new ByteArrayOutputStream
-      val first = true
-      value.foreach { d =>
-        if (!first) {
-          assert( destination_separator.isDefined )
-          baos.write(destination_separator.get)
+      val rc = new StringBuilder
+      value.foreach { dest =>
+        if (rc.length() != 0 ) {
+          assert( destination_separator!=null )
+          rc.append(destination_separator)
         }
-        d match {
+        dest match {
           case d:QueueDestinationDTO =>
-            baos.write(queue_prefix)
+            rc.append(queue_prefix)
           case d:TopicDestinationDTO =>
-            baos.write(topic_prefix)
+            rc.append(topic_prefix)
 //          case Router.TEMP_QUEUE_DOMAIN =>
 //            baos.write(temp_queue_prefix)
 //          case Router.TEMP_TOPIC_DOMAIN =>
 //            baos.write(temp_topic_prefix)
           case _ =>
-            throw new Exception("Uknown destination type: "+d.getClass);
+            throw new Exception("Uknown destination type: "+dest.getClass);
         }
-        ascii(d.name).writeTo(baos)
+        rc.append(encode_path(dest.parts.toIterable))
+
       }
-      baos.toBuffer.ascii
+      rc.toString
     }
   }
 
-  def toString(value:Array[DestinationDTO]) = toBuffer(value).toString
-
   /**
    * Parses a destination which may or may not be a composite.
    *
@@ -94,16 +101,16 @@ class DestinationParser extends PathPars
    * @param compositeSeparator
    * @return
    */
-  def parse(value: AsciiBuffer): Array[DestinationDTO] = {
+  def decode_destination(value: String): Array[DestinationDTO] = {
     if (value == null) {
       return null;
     }
 
-    if (destination_separator.isDefined && value.contains(destination_separator.get)) {
-      var rc = value.split(destination_separator.get);
+    if (destination_separator!=null && value.contains(destination_separator)) {
+      var rc = value.split(Pattern.quote(destination_separator));
       var dl = ListBuffer[DestinationDTO]()
       for (buffer <- rc) {
-        val d = parse(buffer)
+        val d = decode_destination(buffer)
         if (d == null) {
           return null;
         }
@@ -113,11 +120,11 @@ class DestinationParser extends PathPars
     } else {
 
       if (queue_prefix != null && value.startsWith(queue_prefix)) {
-        var name = value.slice(queue_prefix.length, value.length).ascii()
-        return create_destination(LocalRouter.QUEUE_DOMAIN, name.toString)
+        var name = value.substring(queue_prefix.length)
+        return Array( create_destination(LocalRouter.QUEUE_DOMAIN, parts(name)) )
       } else if (topic_prefix != null && value.startsWith(topic_prefix)) {
-        var name = value.slice(topic_prefix.length, value.length).ascii()
-        return create_destination(LocalRouter.TOPIC_DOMAIN, name.toString)
+        var name = value.substring(topic_prefix.length)
+        return Array(create_destination(LocalRouter.TOPIC_DOMAIN, parts(name)))
 //      } else if (temp_queue_prefix != null && value.startsWith(temp_queue_prefix)) {
 //        var name = value.slice(temp_queue_prefix.length, value.length).ascii()
 //        return new DestinationDTO(LocalRouter.TEMP_QUEUE_DOMAIN, name.toString)
@@ -128,7 +135,7 @@ class DestinationParser extends PathPars
         if (default_domain == null) {
           return null;
         }
-        return create_destination(default_domain, value.toString)
+        return Array(create_destination(default_domain, parts(value)))
       }
     }
   }

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/LocalRouter.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/LocalRouter.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/LocalRouter.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/LocalRouter.scala Wed May 25 00:14:03 2011
@@ -23,9 +23,9 @@ import org.apache.activemq.apollo.util._
 import collection.mutable.HashMap
 import org.apache.activemq.apollo.broker.store.QueueRecord
 import Buffer._
-import org.apache.activemq.apollo.util.path.{Path, Part, PathMap, PathParser}
 import java.util.ArrayList
 import org.apache.activemq.apollo.dto._
+import path._
 import security.SecurityContext
 import java.util.concurrent.TimeUnit
 
@@ -35,7 +35,7 @@ trait DomainDestination {
   def virtual_host:VirtualHost
 
   def destination_dto:DestinationDTO
-  def name:String = destination_dto.name
+  def name:String = destination_dto.name(".")
 
   def bind (destination:DestinationDTO, consumer:DeliveryConsumer)
   def unbind (consumer:DeliveryConsumer, persistent:Boolean)
@@ -49,13 +49,16 @@ trait DomainDestination {
  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  */
 object LocalRouter extends Log {
-  val TOPIC_DOMAIN = ascii("topic");
-  val QUEUE_DOMAIN = ascii("queue");
-  val TEMP_TOPIC_DOMAIN = ascii("temp-topic");
-  val TEMP_QUEUE_DOMAIN = ascii("temp-queue");
 
-  val QUEUE_KIND = ascii("queue");
-  val DEFAULT_QUEUE_PATH = ascii("default");
+  val destination_parser = new DestinationParser
+
+  val TOPIC_DOMAIN = "topic"
+  val QUEUE_DOMAIN = "queue"
+  val TEMP_TOPIC_DOMAIN = "temp-topic"
+  val TEMP_QUEUE_DOMAIN = "temp-queue"
+
+  val QUEUE_KIND = "queue"
+  val DEFAULT_QUEUE_PATH = "default"
 
   class ConsumerContext(val destination:DestinationDTO, val consumer:DeliveryConsumer, val security:SecurityContext) {
     override def hashCode: Int = consumer.hashCode
@@ -104,11 +107,7 @@ class LocalRouter(val virtual_host:Virtu
     virtual_host.config.auto_create_destinations.getOrElse(true)
   }
 
-  private val ALL = new Path({
-    val rc = new ArrayList[Part](1)
-    rc.add(Part.ANY_DESCENDANT)
-    rc
-  })
+  private val ALL = new Path(List(AnyDescendantPart))
 
   trait Domain[D <: DomainDestination] {
 
@@ -199,7 +198,7 @@ class LocalRouter(val virtual_host:Virtu
     }
 
     def unbind(destination:DestinationDTO, consumer:DeliveryConsumer, persistent:Boolean) = {
-      val path = DestinationParser.decode_path(destination.name)
+      val path = destination_parser.decode_path(destination.parts)
       if( consumers_by_path.remove(path, new ConsumerContext(destination, consumer, null) ) ) {
         get_destination_matches(path).foreach{ dest=>
           dest.unbind(consumer, persistent)
@@ -263,7 +262,7 @@ class LocalRouter(val virtual_host:Virtu
     }
 
     def disconnect(destination:DestinationDTO, producer:BindableDeliveryProducer) = {
-      val path = DestinationParser.decode_path(destination.name)
+      val path = destination_parser.decode_path(destination.parts)
       producers_by_path.remove(path, new ProducerContext(destination, producer, null))
       get_destination_matches(path).foreach { dest=>
         dest.disconnect(producer)
@@ -302,9 +301,8 @@ class LocalRouter(val virtual_host:Virtu
 
     def topic_config(name:Path):TopicDTO = {
       import collection.JavaConversions._
-      import DestinationParser.default._
-      import AsciiBuffer._
-      virtual_host.config.topics.find( x=> parseFilter(ascii(x.name)).matches(name) ).getOrElse(new TopicDTO)
+      import destination_parser._
+      virtual_host.config.topics.find( x=> decode_filter(x.name).matches(name) ).getOrElse(new TopicDTO)
     }
 
     def can_create_ds(config:DurableSubscriptionDTO, security:SecurityContext) = {
@@ -405,7 +403,7 @@ class LocalRouter(val virtual_host:Virtu
       if( queue.config.unified.getOrElse(false) ) {
         // hook up the queue to be a subscriber of the topic.
 
-        val topic = topic_domain.get_or_create_destination(path, new TopicDestinationDTO(queue.binding.binding_dto.name), null).success
+        val topic = topic_domain.get_or_create_destination(path, new TopicDestinationDTO(queue.binding.binding_dto.parts), null).success
         topic.bind(null, queue)
       }
     }
@@ -417,14 +415,14 @@ class LocalRouter(val virtual_host:Virtu
       import OptionSupport._
       if( queue.config.unified.getOrElse(false) ) {
         // unhook the queue from the topic
-        val topic = topic_domain.get_or_create_destination(path, new TopicDestinationDTO(queue.binding.binding_dto.name), null).success
+        val topic = topic_domain.get_or_create_destination(path, new TopicDestinationDTO(queue.binding.binding_dto.parts), null).success
         topic.unbind(queue, false)
       }
     }
 
     def create_destination(path: Path, destination:DestinationDTO, security: SecurityContext) = {
       val dto = new QueueDestinationDTO
-      dto.name = DestinationParser.encode_path(path)
+      dto.parts.addAll(destination.parts)
 
       val binding = QueueDomainQueueBinding.create(dto)
       val config = binding.config(virtual_host)
@@ -580,7 +578,7 @@ class LocalRouter(val virtual_host:Virtu
 
   def bind(destination: Array[DestinationDTO], consumer: DeliveryConsumer, security: SecurityContext) = {
     consumer.retain
-    val paths = destination.map(x=> (DestinationParser.decode_path(x.name), x) )
+    val paths = destination.map(x=> (destination_parser.decode_path(x.parts), x) )
     dispatch_queue ! {
       val failures = paths.map(x=> domain(x._2).can_bind_all(x._1, x._2, consumer, security) ).flatMap( _.failure_option )
       val rc = if( !failures.isEmpty ) {
@@ -608,7 +606,7 @@ class LocalRouter(val virtual_host:Virtu
 
   def connect(destinations: Array[DestinationDTO], producer: BindableDeliveryProducer, security: SecurityContext) = {
     producer.retain
-    val paths = destinations.map(x=> (DestinationParser.decode_path(x.name), x) )
+    val paths = destinations.map(x=> (destination_parser.decode_path(x.parts), x) )
     dispatch_queue ! {
 
       val failures = paths.map(x=> domain(x._2).can_connect_all(x._1, x._2, producer, security) ).flatMap( _.failure_option )
@@ -643,7 +641,7 @@ class LocalRouter(val virtual_host:Virtu
    * Returns the previously created queue if it already existed.
    */
   def _get_or_create_destination(dto: DestinationDTO, security:SecurityContext): Result[DomainDestination, String] = {
-    val path = DestinationParser.decode_path(dto.name)
+    val path = destination_parser.decode_path(dto.parts)
     domain(dto).get_or_create_destination(path, dto, security)
   }
 

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/QueueBinding.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/QueueBinding.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/QueueBinding.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/QueueBinding.scala Wed May 25 00:14:03 2011
@@ -16,14 +16,14 @@
  */
 package org.apache.activemq.apollo.broker
 
-import org.fusesource.hawtbuf.{Buffer, AsciiBuffer}
 import org.apache.activemq.apollo.selector.SelectorParser
 import org.apache.activemq.apollo.filter.{ConstantExpression, BooleanExpression}
-import Buffer._
 import org.apache.activemq.apollo.dto._
-import org.apache.activemq.apollo.util.{OptionSupport, ClassFinder}
+import org.apache.activemq.apollo.util.ClassFinder
 import org.apache.activemq.apollo.util.path.Path
 import java.lang.String
+import org.fusesource.hawtbuf.{Buffer, AsciiBuffer}
+import Buffer._
 
 /**
  * <p>
@@ -104,22 +104,17 @@ object QueueDomainQueueBinding extends Q
 
   def create(binding_kind:AsciiBuffer, binding_data:Buffer) = {
     if( binding_kind == POINT_TO_POINT_KIND ) {
-      val dto = new QueueDestinationDTO
-      dto.name = binding_data.ascii.toString
+      val dto = JsonCodec.decode(binding_data, classOf[QueueDestinationDTO])
       new QueueDomainQueueBinding(binding_data, dto)
     } else {
       null
     }
   }
 
-  def create(binding_dto:DestinationDTO) = {
-    if( binding_dto.isInstanceOf[QueueDestinationDTO] ) {
-      val ptp_dto = binding_dto.asInstanceOf[QueueDestinationDTO]
-      val data = new AsciiBuffer(ptp_dto.name).buffer
-      new QueueDomainQueueBinding(data, ptp_dto)
-    } else {
-      null
-    }
+  def create(binding_dto:DestinationDTO) = binding_dto match {
+    case ptp_dto:QueueDestinationDTO =>
+      new QueueDomainQueueBinding(JsonCodec.encode(ptp_dto), ptp_dto)
+    case _ => null
   }
 }
 
@@ -134,7 +129,7 @@ class QueueDomainQueueBinding(val bindin
 
   import QueueDomainQueueBinding._
 
-  val destination = DestinationParser.decode_path(binding_dto.name)
+  val destination = LocalRouter.destination_parser.decode_path(binding_dto.parts)
   def binding_kind = POINT_TO_POINT_KIND
 
   def unbind(node: LocalRouter, queue: Queue) = {
@@ -145,7 +140,7 @@ class QueueDomainQueueBinding(val bindin
     node.queue_domain.bind(queue)
   }
 
-  def label = binding_dto.name
+  def label = binding_dto.name(".")
 
   override def hashCode = binding_kind.hashCode ^ binding_data.hashCode
 
@@ -157,10 +152,10 @@ class QueueDomainQueueBinding(val bindin
 
   def config(host:VirtualHost):QueueDTO = {
     import collection.JavaConversions._
-    import DestinationParser.default._
+    import LocalRouter.destination_parser._
 
     def matches(x:QueueDTO):Boolean = {
-      if( x.name != null && !parseFilter(ascii(x.name)).matches(destination)) {
+      if( x.name != null && !decode_filter(x.name).matches(destination)) {
         return false
       }
       true
@@ -201,7 +196,7 @@ object DurableSubscriptionQueueBinding e
 class DurableSubscriptionQueueBinding(val binding_data:Buffer, val binding_dto:DurableSubscriptionDestinationDTO) extends QueueBinding {
   import DurableSubscriptionQueueBinding._
 
-  val destination = DestinationParser.decode_path(binding_dto.name)
+  val destination = LocalRouter.destination_parser.decode_path(binding_dto.parts)
 
   def binding_kind = DURABLE_SUB_KIND
 
@@ -242,11 +237,11 @@ class DurableSubscriptionQueueBinding(va
 
   def config(host:VirtualHost):DurableSubscriptionDTO = {
       import collection.JavaConversions._
-      import DestinationParser.default._
+      import LocalRouter.destination_parser._
       import AsciiBuffer._
 
       def matches(x:DurableSubscriptionDTO):Boolean = {
-        if( x.name != null && !parseFilter(ascii(x.name)).matches(destination)) {
+        if( x.name != null && !decode_filter(x.name).matches(destination)) {
           return false
         }
         if( x.client_id != null && x.client_id!=x.client_id ) {

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/DestinationConfigurationTest.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/DestinationConfigurationTest.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/DestinationConfigurationTest.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/DestinationConfigurationTest.scala Wed May 25 00:14:03 2011
@@ -62,13 +62,15 @@ class DestinationConfigurationTest exten
 
     check_tune_queue_buffer(333) {
       var p = new QueueDestinationDTO()
-      p.name = "unified.a"
+      p.parts.add("unified")
+      p.parts.add("a")
       p
     }
 
     check_tune_queue_buffer(111) {
       var p = new QueueDestinationDTO()
-      p.name = "notunified.other"
+      p.parts.add("notunified")
+      p.parts.add("other")
       p
     }
 

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/perf/BrokerPerfSupport.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/perf/BrokerPerfSupport.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/perf/BrokerPerfSupport.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/broker/perf/BrokerPerfSupport.scala Wed May 25 00:14:03 2011
@@ -215,8 +215,8 @@ abstract class BrokerPerfSupport extends
 
     for (i <- 0 until destCount) {
       val domain = if (PTP) {LocalRouter.QUEUE_DOMAIN} else {LocalRouter.TOPIC_DOMAIN}
-      val name = new AsciiBuffer("dest" + (i + 1))
-      var bean = DestinationParser.create_destination(domain, name.toString)(0)
+      val name ="dest" + (i + 1)
+      var bean = DestinationParser.create_destination(domain, Array(name))
       dests(i) = bean
       //        if (PTP) {
       //          sendBroker.defaultVirtualHost.createQueue(dests(i))

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DestinationDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DestinationDTO.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DestinationDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DestinationDTO.java Wed May 25 00:14:03 2011
@@ -19,6 +19,9 @@ package org.apache.activemq.apollo.dto;
 import org.codehaus.jackson.annotate.JsonTypeInfo;
 
 import javax.xml.bind.annotation.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * <p>
@@ -32,12 +35,28 @@ import javax.xml.bind.annotation.*;
 @XmlAccessorType(XmlAccessType.FIELD)
 abstract public class DestinationDTO {
 
-    @XmlAttribute
-    public String name;
+    @XmlElement(name = "part")
+    public List<String> parts = new ArrayList<String>();
 
     public DestinationDTO() {
     }
-    public DestinationDTO(String name) {
-        this.name = name;
+
+    public DestinationDTO(List<String> parts) {
+        this.parts = parts;
+    }
+
+    public DestinationDTO(String parts[]) {
+        this(Arrays.asList(parts));
+    }
+
+    public String name(String separator) {
+        StringBuilder sb  = new StringBuilder();
+        for( String p : parts ) {
+            if( sb.length() != 0 ) {
+                sb.append(separator);
+            }
+            sb.append(p);
+        }
+        return sb.toString();
     }
 }
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DurableSubscriptionDestinationDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DurableSubscriptionDestinationDTO.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DurableSubscriptionDestinationDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/DurableSubscriptionDestinationDTO.java Wed May 25 00:14:03 2011
@@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlAcce
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
 
 /**
  * <p>
@@ -42,8 +43,10 @@ public class DurableSubscriptionDestinat
 
     public DurableSubscriptionDestinationDTO() {
     }
-
-    public DurableSubscriptionDestinationDTO(String name) {
+    public DurableSubscriptionDestinationDTO(List<String> name) {
+        super(name);
+    }
+    public DurableSubscriptionDestinationDTO(String[] name) {
         super(name);
     }
 
@@ -56,7 +59,7 @@ public class DurableSubscriptionDestinat
 
         if (client_id != null ? !client_id.equals(that.client_id) : that.client_id != null) return false;
         if (filter != null ? !filter.equals(that.filter) : that.filter != null) return false;
-        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (parts != null ? !parts.equals(that.parts) : that.parts != null) return false;
         if (subscription_id != null ? !subscription_id.equals(that.subscription_id) : that.subscription_id != null)
             return false;
 
@@ -65,7 +68,7 @@ public class DurableSubscriptionDestinat
 
     @Override
     public int hashCode() {
-        int result = name != null ? name.hashCode() : 0;
+        int result = parts != null ? parts.hashCode() : 0;
         result = 31 * result + (filter != null ? filter.hashCode() : 0);
         result = 31 * result + (client_id != null ? client_id.hashCode() : 0);
         result = 31 * result + (subscription_id != null ? subscription_id.hashCode() : 0);

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/QueueDestinationDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/QueueDestinationDTO.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/QueueDestinationDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/QueueDestinationDTO.java Wed May 25 00:14:03 2011
@@ -17,6 +17,7 @@
 package org.apache.activemq.apollo.dto;
 
 import javax.xml.bind.annotation.*;
+import java.util.List;
 
 /**
  * <p>
@@ -31,7 +32,10 @@ public class QueueDestinationDTO extends
     public QueueDestinationDTO() {
     }
 
-    public QueueDestinationDTO(String name) {
+    public QueueDestinationDTO(List<String> name) {
+        super(name);
+    }
+    public QueueDestinationDTO(String[] name) {
         super(name);
     }
 
@@ -42,13 +46,13 @@ public class QueueDestinationDTO extends
 
         QueueDestinationDTO that = (QueueDestinationDTO) o;
 
-        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (parts != null ? !parts.equals(that.parts) : that.parts != null) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        return name != null ? name.hashCode() : 0;
+        return parts != null ? parts.hashCode() : 0;
     }
 }
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/TopicDestinationDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/TopicDestinationDTO.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/TopicDestinationDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/TopicDestinationDTO.java Wed May 25 00:14:03 2011
@@ -17,6 +17,8 @@
 package org.apache.activemq.apollo.dto;
 
 import javax.xml.bind.annotation.*;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * <p>
@@ -31,9 +33,13 @@ public class TopicDestinationDTO extends
     public TopicDestinationDTO() {
     }
 
-    public TopicDestinationDTO(String name) {
+    public TopicDestinationDTO(List<String> name) {
         super(name);
     }
+    public TopicDestinationDTO(String name[]) {
+        super(name);
+    }
+
 
 
     @Override
@@ -43,14 +49,14 @@ public class TopicDestinationDTO extends
 
         TopicDestinationDTO that = (TopicDestinationDTO) o;
 
-        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (parts != null ? !parts.equals(that.parts) : that.parts != null) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        return name != null ? name.hashCode() : 0;
+        return parts != null ? parts.hashCode() : 0;
     }
 
 }
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/test/java/org/apache/activemq/apollo/dto/XmlCodecTest.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/test/java/org/apache/activemq/apollo/dto/XmlCodecTest.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/test/java/org/apache/activemq/apollo/dto/XmlCodecTest.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/test/java/org/apache/activemq/apollo/dto/XmlCodecTest.java Wed May 25 00:14:03 2011
@@ -19,7 +19,6 @@ package org.apache.activemq.apollo.dto;
 import org.junit.Test;
 
 import java.io.InputStream;
-import java.util.List;
 
 import static junit.framework.Assert.*;
 

Modified: activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/OpenwireProtocolHandler.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/OpenwireProtocolHandler.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/OpenwireProtocolHandler.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/OpenwireProtocolHandler.scala Wed May 25 00:14:03 2011
@@ -467,7 +467,7 @@ class OpenwireProtocolHandler extends Pr
       if( is_durable_sub ) {
         destination = destination.map { _ match {
           case x:TopicDestinationDTO=>
-            val rc = new DurableSubscriptionDestinationDTO(x.name)
+            val rc = new DurableSubscriptionDestinationDTO(x.parts)
             rc.client_id = parent.parent.info.getClientId
             rc.subscription_id = if( is_durable_sub ) info.getSubscriptionName else null
             rc.filter = info.getSelector

Modified: activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/command/ActiveMQDestination.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/command/ActiveMQDestination.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/command/ActiveMQDestination.java (original)
+++ activemq/activemq-apollo/trunk/apollo-openwire/src/main/scala/org/apache/activemq/apollo/openwire/command/ActiveMQDestination.java Wed May 25 00:14:03 2011
@@ -34,12 +34,7 @@ abstract public class ActiveMQDestinatio
     public static final char COMPOSITE_SEPERATOR = ',';
 
 
-    public static final DestinationParser PARSER = new DestinationParser();
-    static {
-        PARSER.path_seperator = new AsciiBuffer(".");
-        PARSER.any_child_wildcard = new AsciiBuffer("*");
-        PARSER.any_descendant_wildcard = new AsciiBuffer(">");
-    }
+    public static final DestinationParser PARSER = DestinationParser.OPENWIRE_PARSER();
 
     public static final byte QUEUE_TYPE = 0x01;
     public static final byte TOPIC_TYPE = 0x02;
@@ -138,15 +133,15 @@ abstract public class ActiveMQDestinatio
         return physicalName;
     }
 
-    DestinationDTO[] create_destination(AsciiBuffer domain, Path path) {
-        return DestinationParser.create_destination(domain, DestinationParser.encode_path(path));
+    DestinationDTO[] create_destination(String domain, Path path) {
+        return new DestinationDTO[] { PARSER.create_destination(domain, PARSER.path_parts(path)) };
     }
 
     public void setPhysicalName(String value) {
         physicalName = value;
         String[] composites = value.split(",");
         if(composites.length == 1) {
-            Path path = PARSER.parsePath(new AsciiBuffer(composites[0]));
+            Path path = PARSER.decode_path(composites[0]);
             switch(getDestinationType()) {
                 case QUEUE_TYPE:
                     destination = create_destination(LocalRouter.QUEUE_DOMAIN(), path);

Modified: activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompFrame.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompFrame.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompFrame.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompFrame.scala Wed May 25 00:14:03 2011
@@ -65,19 +65,12 @@ case class StompFrameMessage(frame:Stomp
    */
   var persistent = false
 
-  /**
-   * where the message was sent to.
-   */
-  var destination: Array[DestinationDTO] = null
-
   for( header <- (frame.updated_headers ::: frame.headers).reverse ) {
     header match {
       case (MESSAGE_ID, value) =>
         id = value
       case (PRIORITY, value) =>
         priority = java.lang.Integer.parseInt(value).toByte
-      case (DESTINATION, value) =>
-        destination = value
       case (EXPIRATION_TIME, value) =>
         expiration = java.lang.Long.parseLong(value)
       case (PERSISTENT, value) =>
@@ -302,25 +295,16 @@ object Stomp {
   val DURABLE_PREFIX = ascii("durable:")
   val DURABLE_QUEUE_KIND = ascii("stomp:sub")
 
-
   val destination_parser = new DestinationParser
-  destination_parser.queue_prefix = ascii(System.getProperty("apollo.stomp.queue_prefix", "/queue/"))
-  destination_parser.topic_prefix = ascii(System.getProperty("apollo.stomp.topic_prefix","/topic/"))
-  destination_parser.destination_separator = Some(System.getProperty("apollo.stomp.destination_separator",",").charAt(0).toByte)
-  destination_parser.path_seperator = ascii(System.getProperty("apollo.stomp.path_seperator","."))
-  destination_parser.any_child_wildcard = ascii(System.getProperty("apollo.stomp.any_child_wildcard","*"))
-  destination_parser.any_descendant_wildcard = ascii(System.getProperty("apollo.stomp.any_descendant_wildcard","**"))
+  destination_parser.queue_prefix = "/queue/"
+  destination_parser.topic_prefix = "/topic/"
+  destination_parser.destination_separator = ","
+  destination_parser.path_separator = "."
+  destination_parser.any_child_wildcard = "*"
+  destination_parser.any_descendant_wildcard = "**"
 
   destination_parser.default_domain = LocalRouter.QUEUE_DOMAIN
 
-  implicit def toDestinationDTO(value:AsciiBuffer):Array[DestinationDTO] = {
-    val d = destination_parser.parse(value)
-    if( d==null ) {
-      throw new ProtocolException("Invalid stomp destiantion name: "+value);
-    }
-    d
-  }
-
   type HeaderMap = List[(AsciiBuffer, AsciiBuffer)]
   type HeaderMapBuffer = ListBuffer[(AsciiBuffer, AsciiBuffer)]
   val NO_DATA = new Buffer(0);

Modified: activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompProtocolHandler.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompProtocolHandler.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompProtocolHandler.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/StompProtocolHandler.scala Wed May 25 00:14:03 2011
@@ -368,6 +368,16 @@ class StompProtocolHandler extends Proto
 
   var protocol_filters = List[ProtocolFilter]()
 
+  var destination_parser = Stomp.destination_parser
+
+  implicit def toDestinationDTO(value:AsciiBuffer):Array[DestinationDTO] = {
+    val rc = destination_parser.decode_destination(value.toString)
+    if( rc==null ) {
+      throw new ProtocolException("Invalid stomp destiantion name: "+value);
+    }
+    rc
+  }
+
   override def set_connection(connection: BrokerConnection) = {
     super.set_connection(connection)
     import collection.JavaConversions._
@@ -381,6 +391,24 @@ class StompProtocolHandler extends Proto
     config.max_data_length.foreach( codec.max_data_length = _ )
     config.max_header_length.foreach( codec.max_header_length = _ )
     config.max_headers.foreach( codec.max_headers = _ )
+
+    if( config.queue_prefix!=null ||
+        config.topic_prefix!=null ||
+        config.destination_separator!=null ||
+        config.path_separator!= null ||
+        config.any_child_wildcard != null ||
+        config.any_descendant_wildcard!= null ) {
+
+      destination_parser = new DestinationParser().copy(Stomp.destination_parser)
+      if( config.queue_prefix!=null ) { destination_parser.queue_prefix = config.queue_prefix }
+      if( config.topic_prefix!=null ) { destination_parser.topic_prefix = config.topic_prefix }
+      if( config.destination_separator!=null ) { destination_parser.destination_separator = config.destination_separator }
+      if( config.path_separator!=null ) { destination_parser.path_separator = config.path_separator }
+      if( config.any_child_wildcard!=null ) { destination_parser.any_child_wildcard = config.any_child_wildcard }
+      if( config.any_descendant_wildcard!=null ) { destination_parser.any_descendant_wildcard = config.any_descendant_wildcard }
+
+    }
+
   }
 
   override def create_connection_status = {
@@ -895,7 +923,7 @@ class StompProtocolHandler extends Proto
     if( persistent ) {
       destination = destination.map { _ match {
         case x:TopicDestinationDTO=>
-          val rc = new DurableSubscriptionDestinationDTO(x.name)
+          val rc = new DurableSubscriptionDestinationDTO(x.parts)
           rc.subscription_id = decode_header(id)
           rc.filter = if (selector == null) null else selector._1
           rc

Modified: activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/dto/StompDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/dto/StompDTO.java?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/dto/StompDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/main/scala/org/apache/activemq/apollo/stomp/dto/StompDTO.java Wed May 25 00:14:03 2011
@@ -56,4 +56,22 @@ public class StompDTO extends ProtocolDT
     @XmlElement(name="protocol_filter")
     public List<String> protocol_filters = new ArrayList<String>();
 
+    @XmlAttribute(name="queue_prefix")
+    public String queue_prefix;
+
+    @XmlAttribute(name="topic_prefix")
+    public String topic_prefix;
+
+    @XmlAttribute(name="destination_separator")
+    public String destination_separator;
+
+    @XmlAttribute(name="path_separator")
+    public String path_separator;
+
+    @XmlAttribute(name="any_child_wildcard")
+    public String any_child_wildcard;
+
+    @XmlAttribute(name="any_descendant_wildcard")
+    public String any_descendant_wildcard;
+
 }

Added: activemq/activemq-apollo/trunk/apollo-stomp/src/test/resources/apollo-stomp-custom-dest-delimiters.xml
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/test/resources/apollo-stomp-custom-dest-delimiters.xml?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/test/resources/apollo-stomp-custom-dest-delimiters.xml (added)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/test/resources/apollo-stomp-custom-dest-delimiters.xml Wed May 25 00:14:03 2011
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<broker xmlns="http://activemq.apache.org/schema/activemq/apollo">
+  <notes>This broker configuration is what the unit tests in this module load up.</notes>
+
+  <virtual_host id="default" purge_on_startup="true" auto_create_queues="true">
+    <host_name>localhost</host_name>
+
+    <queue name="unified.**" unified="true"/>
+
+  </virtual_host>
+
+  <connector id="tcp" protocol="stomp" bind="tcp://0.0.0.0:0">
+    <stomp path_separator="/"/>
+  </connector>
+
+</broker>
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/StompTest.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/StompTest.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/StompTest.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/StompTest.scala Wed May 25 00:14:03 2011
@@ -1303,3 +1303,49 @@ class StompSslSecurityTest extends Stomp
   }
 
 }
+
+class StompWildcardTest extends StompTestSupport {
+
+  def path_separator = "."
+
+  test("Wildcard subscription") {
+    connect("1.1")
+
+    client.write(
+      "SUBSCRIBE\n" +
+      "destination:/queue/foo"+path_separator+"*\n" +
+      "id:1\n" +
+      "receipt:0\n"+
+      "\n")
+
+    wait_for_receipt("0")
+
+    def put(dest:String) = {
+      client.write(
+        "SEND\n" +
+        "destination:/queue/"+dest+"\n" +
+        "\n" +
+        "message:"+dest+"\n")
+    }
+
+    def get(dest:String) = {
+      val frame = client.receive()
+      frame should startWith("MESSAGE\n")
+      frame should endWith("\n\nmessage:%s\n".format(dest))
+    }
+
+    // We should not get this one..
+    put("bar"+path_separator+"a")
+
+    put("foo"+path_separator+"a")
+    get("foo"+path_separator+"a")
+
+    put("foo"+path_separator+"b")
+    get("foo"+path_separator+"b")
+  }
+}
+
+class CustomStompWildcardTest extends StompWildcardTest {
+  override val broker_config_uri: String = "xml:classpath:apollo-stomp-custom-dest-delimiters.xml"
+  override def path_separator = "/"
+}

Modified: activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/perf/StompRemoteClients.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/perf/StompRemoteClients.scala?rev=1127340&r1=1127339&r2=1127340&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/perf/StompRemoteClients.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-stomp/src/test/scala/org/apache/activemq/apollo/stomp/perf/StompRemoteClients.scala Wed May 25 00:14:03 2011
@@ -39,8 +39,8 @@ class StompRemoteConsumer extends Remote
     outboundSink.refiller = ^ {}
 
     val stompDestination = destination match {
-      case x:QueueDestinationDTO => ascii("/queue/" + x.name);
-      case x:TopicDestinationDTO => ascii("/topic/" + x.name);
+      case x:QueueDestinationDTO => ascii("/queue/" + x.parts);
+      case x:TopicDestinationDTO => ascii("/topic/" + x.parts);
     }
 
     var frame = StompFrame(CONNECT);
@@ -149,8 +149,8 @@ class StompRemoteProducer extends Remote
     outboundSink.refiller = ^ {drain}
 
     stompDestination = destination match {
-      case x:QueueDestinationDTO => ascii("/queue/" + x.name);
-      case x:TopicDestinationDTO => ascii("/topic/" + x.name);
+      case x:QueueDestinationDTO => ascii("/queue/" + x.parts);
+      case x:TopicDestinationDTO => ascii("/topic/" + x.parts);
     }
 
     outboundSink.offer(StompFrame(CONNECT));

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/AnyChildPathNode.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,116 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.ArrayList
+import java.util.Collection
+import java.util.Set
+import collection.JavaConversions._
+
+/**
+  * An implementation of {@link PathNode} which navigates all the children of the given node
+  * ignoring the name of the current path (so for navigating using * in a wildcard).
+  *
+  */
+class AnyChildPathNode[Value] extends PathNode[Value] {
+  def this(node: PathNode[Value]) {
+    this ()
+    this.node = node
+  }
+
+  def appendMatchingValues(answer: Set[Value], path: Path, startIndex: Int): Unit = {
+    for (child <- getChildNodes) {
+      child.appendMatchingValues(answer, path, startIndex)
+    }
+  }
+
+  def appendMatchingWildcards(answer: Set[Value], path: Path, startIndex: Int): Unit = {
+    for (child <- getChildNodes) {
+      child.appendMatchingWildcards(answer, path, startIndex)
+    }
+  }
+
+  def appendDescendantValues(answer: Set[Value]): Unit = {
+    for (child <- getChildNodes) {
+      child.appendDescendantValues(answer)
+    }
+  }
+
+  def getChild(part: Part): PathNode[Value] = {
+    val list: Collection[PathNode[Value]] = new ArrayList[PathNode[Value]]
+    for (child <- getChildNodes) {
+      var answer: PathNode[Value] = child.getChild(part)
+      if (answer != null) {
+        list.add(answer)
+      }
+    }
+    if (!list.isEmpty) {
+      return new AnyChildPathNode[Value]((this)) {
+        protected override def getChildNodes: Collection[PathNode[Value]] = {
+          return list
+        }
+      }
+    }
+    return null
+  }
+
+  def getDesendentValues: Collection[Value] = {
+    var answer: Collection[Value] = new ArrayList[Value]
+    for (child <- getChildNodes) {
+      answer.addAll(child.getDesendentValues)
+    }
+    return answer
+  }
+
+  def getValues: Collection[Value] = {
+    var answer: Collection[Value] = new ArrayList[Value]
+    for (child <- getChildNodes) {
+      answer.addAll(child.getValues)
+    }
+    return answer
+  }
+
+  def getChildren: Collection[PathNode[Value]] = {
+    var answer: Collection[PathNode[Value]] = new ArrayList[PathNode[Value]]
+    for (child <- getChildNodes) {
+      answer.addAll(child.getChildren)
+    }
+    return answer
+  }
+
+  def removeDesendentValues: Collection[Value] = {
+    var answer: Collection[Value] = new ArrayList[Value]
+    for (child <- getChildNodes) {
+      answer.addAll(child.removeDesendentValues)
+    }
+    return answer
+  }
+
+  def removeValues: Collection[Value] = {
+    var answer: Collection[Value] = new ArrayList[Value]
+    for (child <- getChildNodes) {
+      answer.addAll(child.removeValues)
+    }
+    return answer
+  }
+
+  protected def getChildNodes: Collection[PathNode[Value]] = {
+    return node.getChildren
+  }
+
+  private var node: PathNode[Value] = null
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Part.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,43 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+/**
+  * Holds the delimiters used to parse paths.
+  *
+  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+  */
+sealed trait Part {
+  def matches(p: Part) = true
+}
+
+object RootPart extends Part {
+  override def matches(p: Part) = p match {
+    case RootPart => true
+    case _ => false
+  }
+}
+
+object AnyChildPart extends Part
+object AnyDescendantPart extends Part
+
+case class LiteralPart(value: String) extends Part {
+  override def matches(p: Part) = p match {
+    case LiteralPart(v) => v == value
+    case _ => true
+  }
+}

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/Path.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,35 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+/**
+  * <p>
+  * </p>
+  *
+  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+  */
+case class Path(parts: List[Part]) {
+
+  override def toString: String = {
+    return toString(PathParser.DEFAULT)
+  }
+
+  def toString(pp: PathParser): String = {
+    return pp.encode_path(this)
+  }
+
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathFilter.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,26 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+/**
+  * Represents a filter which only operates on a path
+  *
+  * @version $Revision: 1.3 $
+  */
+abstract trait PathFilter {
+  def matches(path: Path): Boolean
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMap.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,99 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.HashSet
+import java.util.Set
+import java.util.SortedSet
+import java.util.TreeSet
+
+/**
+  * A Map-like data structure allowing values to be indexed by
+  * {@link String} and retrieved by path - supporting both *
+  * and &gt; style of wildcard as well as composite paths. <br>
+  * This class assumes that the index changes rarely but that fast lookup into
+  * the index is required. So this class maintains a pre-calculated index for
+  * path steps. So looking up the values for "TEST.*" or "*.TEST" will be
+  * pretty fast. <br>
+  * Looking up of a value could return a single value or a List of matching
+  * values if a wildcard or composite path is used.
+  *
+  * @version $Revision: 1.3 $
+  */
+class PathMap[Value] {
+  /**
+    * Looks up the value(s) matching the given String key. For simple
+    * paths this is typically a List of one single value, for wild cards
+    * or composite paths this will typically be a List of matching
+    * values.
+    *
+    * @param key the path to lookup
+    * @return a List of matching values or an empty list if there are no
+    *         matching values.
+    */
+  def get(key: Path): Set[Value] = {
+    return findWildcardMatches(key)
+  }
+
+  def put(key: Path, value: Value): Unit = {
+    root.add(key, 0, value)
+  }
+
+  /**
+    * Removes the value from the associated path
+    */
+  def remove(path: Path, value: Value): Boolean = {
+    return root.remove(path, 0, value)
+  }
+
+  def getRootNode = root
+
+  protected def findWildcardMatches(path: Path): Set[Value] = {
+    var answer: HashSet[Value] = new HashSet[Value]
+    root.appendMatchingValues(answer, path, 0)
+    return answer
+  }
+
+  /**
+    * @param key
+    * @return
+    */
+  def removeAll(key: Path): Set[Value] = {
+    var rc: HashSet[Value] = new HashSet[Value]
+    root.removeAll(rc, key, 0)
+    return rc
+  }
+
+  /**
+    * Returns the value which matches the given path or null if there is
+    * no matching value. If there are multiple values, the results are sorted
+    * and the last item (the biggest) is returned.
+    *
+    * @param path the path to find the value for
+    * @return the largest matching value or null if no value matches
+    */
+  def chooseValue(path: Path): Value = {
+    var set: Set[Value] = get(path)
+    if ((set == null) || set.isEmpty) {
+      return null.asInstanceOf[Value]
+    }
+    var sortedSet: SortedSet[Value] = new TreeSet[Value](set)
+    return sortedSet.last
+  }
+
+  private final val root = new PathMapNode[Value](null)
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathMapNode.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,242 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.ArrayList
+import java.util.Collection
+import java.util.HashMap
+import java.util.HashSet
+import java.util.List
+import java.util.Map
+import java.util.Set
+import collection.JavaConversions._
+
+/**
+  * An implementation class used to implement {@link PathMap}
+  *
+  * @version $Revision: 1.2 $
+  */
+class PathMapNode[Value] extends PathNode[Value] {
+  def this(parent: PathMapNode[Value]) {
+    this ()
+    this.parent = parent
+    if (parent == null) {
+      pathLength = 0
+    }
+    else {
+      pathLength = parent.pathLength + 1
+    }
+  }
+
+  /**
+    * Returns the child node for the given named path or null if it does not
+    * exist
+    */
+  def getChild(part: Part): PathMapNode[Value] = {
+    return childNodes.get(part).asInstanceOf[PathMapNode[Value]]
+  }
+
+  /**
+    * Returns the child nodes
+    */
+  def getChildren: Collection[PathNode[Value]] = {
+    return childNodes.values
+  }
+
+  def getChildCount: Int = {
+    return childNodes.size
+  }
+
+  /**
+    * Returns the child node for the given named path, lazily creating one if
+    * it does not yet exist
+    */
+  def getChildOrCreate(part: Part): PathMapNode[Value] = {
+    var answer: PathMapNode[Value] = childNodes.get(part).asInstanceOf[PathMapNode[Value]]
+    if (answer == null) {
+      answer = createChildNode
+      answer.part = part
+      childNodes.put(part, answer)
+    }
+    return answer
+  }
+
+  /**
+    * Returns a mutable List of the values available at this node in the tree
+    */
+  def getValues: List[Value] = {
+    return values
+  }
+
+  /**
+    * Returns a mutable List of the values available at this node in the tree
+    */
+  def removeValues: List[Value] = {
+    var v: ArrayList[Value] = new ArrayList[Value](values)
+    values.clear
+    pruneIfEmpty
+    return v
+  }
+
+  def removeDesendentValues: Set[Value] = {
+    var answer: Set[Value] = new HashSet[Value]
+    removeDesendentValues(answer)
+    return answer
+  }
+
+  protected def removeDesendentValues(answer: Set[Value]): Unit = {
+    answer.addAll(removeValues)
+  }
+
+  /**
+    * Returns a list of all the values from this node down the tree
+    */
+  def getDesendentValues: Set[Value] = {
+    var answer: Set[Value] = new HashSet[Value]
+    appendDescendantValues(answer)
+    return answer
+  }
+
+  def add(path: Path, idx: Int, value: Value): Unit = {
+    if (idx >= path.parts.size) {
+      values.add(value)
+    }
+    else {
+      getChildOrCreate(path.parts.get(idx)).add(path, idx + 1, value)
+    }
+  }
+
+  def remove(path: Path, idx: Int, value: Value): Boolean = {
+    if (idx >= path.parts.size) {
+      var rc = values.remove(value)
+      pruneIfEmpty
+      return rc
+    } else {
+      return getChildOrCreate(path.parts.get(idx)).remove(path, idx+1, value)
+    }
+  }
+
+  def removeAll(answer: Set[Value], path: Path, startIndex: Int): Unit = {
+    var node: PathNode[Value] = this
+    var size: Int = path.parts.size;
+    var i: Int = startIndex
+
+    while (i < size && node != null) {
+       path.parts.get(i) match {
+        case AnyDescendantPart =>
+          answer.addAll(node.removeDesendentValues)
+          i = size;
+        case AnyChildPart =>
+          node.appendMatchingWildcards(answer, path, i)
+          node = new AnyChildPathNode[Value](node)
+          i += 1;
+        case part =>
+          node.appendMatchingWildcards(answer, path, i)
+          node = node.getChild(part)
+          i += 1;
+      }
+    }
+    if (node != null) {
+      answer.addAll(node.removeValues)
+    }
+  }
+
+  def appendDescendantValues(answer: Set[Value]): Unit = {
+    answer.addAll(values)
+    for (child <- childNodes.values) {
+      child.appendDescendantValues(answer)
+    }
+  }
+
+  /**
+    * Factory method to create a child node
+    */
+  protected def createChildNode: PathMapNode[Value] = {
+    return new PathMapNode[Value](this)
+  }
+
+  /**
+    * Matches any entries in the map containing wildcards
+    */
+  def appendMatchingWildcards(answer: Set[Value], parts: Path, idx: Int): Unit = {
+    if (idx - 1 > pathLength) {
+      return
+    }
+    var wildCardNode: PathMapNode[Value] = getChild(AnyChildPart)
+    if (wildCardNode != null) {
+      wildCardNode.appendMatchingValues(answer, parts, idx + 1)
+    }
+    wildCardNode = getChild(AnyDescendantPart)
+    if (wildCardNode != null) {
+      answer.addAll(wildCardNode.getDesendentValues)
+    }
+  }
+
+  def appendMatchingValues(answer: Set[Value], path: Path, startIndex: Int): Unit = {
+    var node: PathNode[Value] = this
+    var couldMatchAny: Boolean = true
+    var size: Int = path.parts.size;
+    var i: Int = startIndex
+    while (i < size && node != null) {
+      var part: Part = path.parts.get(i)
+      if (part == AnyDescendantPart) {
+        answer.addAll(node.getDesendentValues)
+        couldMatchAny = false
+        i = size
+      } else {
+        node.appendMatchingWildcards(answer, path, i)
+        if (part == AnyChildPart) {
+          node = new AnyChildPathNode[Value](node)
+        }
+        else {
+          node = node.getChild(part)
+        }
+        i += 1;
+      }
+    }
+    if (node != null) {
+      answer.addAll(node.getValues)
+      if (couldMatchAny) {
+        var child: PathNode[Value] = node.getChild(AnyDescendantPart)
+        if (child != null) {
+          answer.addAll(child.getValues)
+        }
+      }
+    }
+  }
+
+  def getPart: Part = {
+    return part
+  }
+
+  protected def pruneIfEmpty: Unit = {
+    if (parent != null && childNodes.isEmpty && values.isEmpty) {
+      parent.removeChild(this)
+    }
+  }
+
+  protected def removeChild(node: PathMapNode[Value]): Unit = {
+    childNodes.remove(node.getPart)
+    pruneIfEmpty
+  }
+
+  private var parent: PathMapNode[Value] = null
+  private var values: List[Value] = new ArrayList[Value]
+  private var childNodes: Map[Part, PathNode[Value]] = new HashMap[Part, PathNode[Value]]
+  private var part: Part = RootPart
+  private var pathLength: Int = 0
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathNode.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,44 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.Collection
+import java.util.Set
+
+/**
+  * Represents a node in the {@link PathMap} tree
+  *
+  */
+abstract trait PathNode[Value] {
+  def appendMatchingValues(answer: Set[Value], path: Path, startIndex: Int): Unit
+
+  def appendMatchingWildcards(answer: Set[Value], path: Path, startIndex: Int): Unit
+
+  def appendDescendantValues(answer: Set[Value]): Unit
+
+  def getDesendentValues: Collection[Value]
+
+  def getChild(part: Part): PathNode[Value]
+
+  def getValues: Collection[Value]
+
+  def getChildren: Collection[PathNode[Value]]
+
+  def removeDesendentValues: Collection[Value]
+
+  def removeValues: Collection[Value]
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/path/PathParser.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,195 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.LinkedList
+import java.util.regex._
+import collection.JavaConversions._
+import org.apache.activemq.apollo.util.path.PathParser.PartFilter
+/**
+  * Holds the delimiters used to parse paths.
+  *
+  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+  */
+object PathParser {
+  def containsWildCards(path: Path): Boolean = {
+    for (p <- path.parts) {
+      p match {
+        case AnyDescendantPart =>
+          return true
+        case AnyChildPart =>
+          return true
+        case _ =>
+      }
+    }
+    return false
+  }
+
+  val DEFAULT = new PathParser
+
+  class PathException(message: String) extends RuntimeException(message) 
+  
+  trait PartFilter {
+    def matches(remaining: LinkedList[Part]): Boolean
+  }
+
+  class AnyChildPathFilter(val next: PartFilter) extends PartFilter {
+
+    def matches(remaining: LinkedList[Part]): Boolean = {
+      if (!remaining.isEmpty) {
+        var p: Part = remaining.removeFirst
+        if (next != null) {
+          return next.matches(remaining)
+        }
+        else {
+          return remaining.isEmpty
+        }
+      }
+      else {
+        return false
+      }
+    }
+
+  }
+
+  class AnyDecendentPathFilter(val next: PartFilter) extends PartFilter {
+    def matches(remaining: LinkedList[Part]): Boolean = {
+      if (!remaining.isEmpty) {
+        remaining.clear
+        return true
+      }
+      else {
+        return false
+      }
+    }
+  }
+
+}
+
+class PathParser {
+
+  var any_descendant_wildcard = "**"
+  var any_child_wildcard = "*"
+  var path_separator = "."
+  var part_pattern = Pattern.compile("[a-zA-Z0-9\\_\\-\\%\\~]+")
+
+  def copy(other:PathParser) = {
+    any_descendant_wildcard = other.any_descendant_wildcard
+    any_child_wildcard = other.any_child_wildcard
+    path_separator = other.path_separator
+    part_pattern = other.part_pattern
+    this
+  }
+
+  def decode_path(subject: java.util.Collection[String]): Path = decode_path(subject.toIterable)
+
+  def decode_path(subject: Iterable[String]): Path = {
+    return new Path(subject.toList.map(decode_part(_)))
+  }
+
+  def parts(subject: String): Array[String] = {
+    subject.split(Pattern.quote(path_separator))
+  }
+
+  def decode_path(subject: String): Path = {
+    return decode_path(parts(subject))
+  }
+
+  private def decode_part(value: String): Part = {
+    if (value == any_child_wildcard) {
+      return AnyChildPart
+    } else if (value == any_descendant_wildcard) {
+      return AnyDescendantPart
+    } else {
+      if (part_pattern == null || part_pattern.matcher(value.toString).matches) {
+        return LiteralPart(value)
+      } else {
+        throw new PathParser.PathException(String.format("Invalid destination path part: '%s', it does not match regex: %s", value, part_pattern))
+      }
+    }
+  }
+
+  /**
+    * Converts the path back to the string representation.
+    * @return
+    */
+  def encode_path(path: Path): String = encode_path(path_parts(path))
+
+  def path_parts(path: Path):Array[String] = {
+    (path.parts.map( _ match {
+      case RootPart => ""
+      case AnyChildPart => any_child_wildcard
+      case AnyDescendantPart => any_descendant_wildcard
+      case LiteralPart(value) => value
+    })).toArray
+  }
+
+  def encode_path(parts: Iterable[String]): String = {
+    var buffer: StringBuffer = new StringBuffer
+    for (p <- parts) {
+      if ( buffer.length() != 0) {
+        buffer.append(path_separator)
+      }
+      buffer.append(p)
+    }
+    return buffer.toString
+  }
+
+  def decode_filter(path: String): PathFilter = {
+    var last: PathParser.PartFilter = null
+    for (p <- decode_path(path).parts.reverse ) {
+      p match {
+        case p:LiteralPart =>
+          last = new LitteralPathFilter(last, p)
+        case AnyChildPart =>
+          last = new PathParser.AnyChildPathFilter(last)
+        case AnyDescendantPart =>
+          last = new PathParser.AnyDecendentPathFilter(last)
+        case _ =>
+      }
+    }
+    val filter: PathParser.PartFilter = last
+    return new PathFilter {
+      def matches(path: Path): Boolean = {
+        return filter.matches(new LinkedList[Part](path.parts))
+      }
+    }
+  }
+
+  class LitteralPathFilter(val next: PartFilter, val path: LiteralPart) extends PartFilter {
+
+    def matches(remaining: LinkedList[Part]): Boolean = {
+      if (!remaining.isEmpty) {
+        var p: Part = remaining.removeFirst
+        if (!path.matches(p)) {
+          return false
+        }
+        if (next != null) {
+          return next.matches(remaining)
+        }
+        else {
+          return remaining.isEmpty
+        }
+      }
+      else {
+        return false
+      }
+    }
+
+  }
+
+}
\ No newline at end of file

Added: activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.scala?rev=1127340&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/apache/activemq/apollo/util/path/PathMapMemoryTest.scala Wed May 25 00:14:03 2011
@@ -0,0 +1,86 @@
+/**
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+package org.apache.activemq.apollo.util.path
+
+import java.util.Set
+import org.junit.Test
+import org.junit.Assert._
+
+/**
+  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+  */
+class PathMapMemoryTest {
+  protected def createDestination(name: String): Path = {
+    return parser.decode_path(name)
+  }
+
+  @Test def testLongPath: Unit = {
+    var d1: Path = createDestination("1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18")
+    var map: PathMap[String] = new PathMap[String]
+    map.put(d1, "test")
+  }
+
+  @Test def testVeryLongPaths: Unit = {
+    {
+      var i: Int = 1
+      while (i < 100) {
+        var name: String = "1"
+        var j: Int = 2
+        while (j <= i) {
+          name += "." + j
+          j += 1; j
+        }
+        try {
+          var d1: Path = createDestination(name)
+          var map: PathMap[String] = new PathMap[String]
+          map.put(d1, "test")
+        } catch {
+          case e: Throwable => {
+            fail(("Destination name too long: " + name + " : " + e))
+          }
+        }
+        i += 1; i
+      }
+    }
+  }
+
+  @Test def testLotsOfPaths: Unit = {
+    var map: PathMap[AnyRef] = new PathMap[AnyRef]
+    var value: AnyRef = new AnyRef
+    var count: Int = 1000;
+    {
+      var i: Int = 0
+      while (i < count) {
+          var queue: Path = createDestination("connection-" + i)
+          map.put(queue, value)
+          i += 1; i
+      }
+    }
+    {
+      var i: Int = 0
+      while (i < count) {
+          var queue: Path = createDestination("connection-" + i)
+          map.remove(queue, value)
+          var set: Set[AnyRef] = map.get(queue)
+          assertTrue(set.isEmpty)
+          i += 1; i
+      }
+    }
+  }
+
+  private[path] var parser: PathParser = new PathParser
+}
\ No newline at end of file



Mime
View raw message