Author: rajith
Date: Thu May 23 22:27:03 2013
New Revision: 1485878

URL: http://svn.apache.org/r1485878
Log:
QPID-4873 Commiting patch by Helen Kwong.

Modified:
    
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java
    
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java
    
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Node.java
    
qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/client/AMQDestinationTest.java
    
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java
    
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/AddressParser.java
    
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/Parser.java

Modified: 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java
 (original)
+++ 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java
 Thu May 23 22:27:03 2013
@@ -123,10 +123,10 @@ public class AddressHelper
     @SuppressWarnings("unchecked")
     public List<Binding> getBindings(Map props)
     {
-        List<Binding> bindings = new ArrayList<Binding>();
         List<Map> bindingList = (props == null) ? Collections.EMPTY_LIST : 
(List<Map>) props.get(X_BINDINGS);
-        if (bindingList != null)
+        if (bindingList != null && !bindingList.isEmpty())
         {
+            List<Binding> bindings = new 
ArrayList<Binding>(bindingList.size());
             for (Map bindingMap : bindingList)
             {
                 Binding binding = new Binding(
@@ -138,8 +138,12 @@ public class AddressHelper
                                         .get(ARGUMENTS));
                 bindings.add(binding);
             }
+            return bindings;
+        }
+        else
+        {
+            return Collections.emptyList();
         }
-        return bindings;
     }
 
     public Map getDeclareArgs(Map props)

Modified: 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java
 (original)
+++ 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java
 Thu May 23 22:27:03 2013
@@ -20,7 +20,6 @@
  */
 package org.apache.qpid.client.messaging.address;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -43,7 +42,7 @@ public class Link
     private int _producerCapacity = 0;
     private Subscription subscription;
     private Reliability reliability = Reliability.AT_LEAST_ONCE;
-    private List<Binding> _bindings = new ArrayList<Binding>();
+    private List<Binding> _bindings = Collections.emptyList();
     private SubscriptionQueue _subscriptionQueue;
 
     public Reliability getReliability()
@@ -206,7 +205,7 @@ public class Link
     
     public static class Subscription
     {
-        private Map<String,Object> args = new HashMap<String,Object>();        
+        private Map<String,Object> args = Collections.emptyMap();
         private boolean exclusive = false;
         
         public Map<String, Object> getArgs()

Modified: 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Node.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Node.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Node.java
 (original)
+++ 
qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Node.java
 Thu May 23 22:27:03 2013
@@ -21,15 +21,14 @@
 
 package org.apache.qpid.client.messaging.address;
 
-import org.apache.qpid.client.AMQDestination;
-import org.apache.qpid.client.AMQDestination.Binding;
-
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQDestination.Binding;
+
 public class Node
 { 
     private int _nodeType = AMQDestination.UNKNOWN_TYPE;
@@ -39,7 +38,7 @@ public class Node
     private boolean _isExclusive;
     private String _alternateExchange;
     private String _exchangeType = "topic"; // used when node is an exchange 
instead of a queue.
-    private List<Binding> _bindings = new ArrayList<Binding>();
+    private List<Binding> _bindings = Collections.emptyList();
     private Map<String,Object> _declareArgs = new HashMap<String,Object>();
 
     protected Node(String name)
@@ -112,10 +111,6 @@ public class Node
         _bindings = bindings;
     }
     
-    public void addBinding(Binding binding) {
-        this._bindings.add(binding);
-    }
-    
     public Map<String,Object> getDeclareArgs()
     {
         return _declareArgs;

Modified: 
qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/client/AMQDestinationTest.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/client/AMQDestinationTest.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/client/AMQDestinationTest.java
 (original)
+++ 
qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/client/AMQDestinationTest.java
 Thu May 23 22:27:03 2013
@@ -20,6 +20,10 @@
  */
 package org.apache.qpid.client;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
 import junit.framework.TestCase;
 
 public class AMQDestinationTest extends TestCase
@@ -63,4 +67,95 @@ public class AMQDestinationTest extends 
         assertTrue(dest7.hashCode() != dest8.hashCode());
         assertTrue(dest6.hashCode() != dest9.hashCode());
     }
+
+    /**
+     * Tests that destinations created with the same options string will share 
the same address options map.
+     */
+    public void testCacheAddressOptionsMaps() throws Exception
+    {
+        // Create destinations 1 and 3 with the same options string, and 
destinations 2 and 4 with a different one
+        String optionsStringA = "{create: always, node: {type: topic}}";
+        String optionsStringB = "{}";   // empty options
+        AMQDestination dest1 = createDestinationWithOptions("testDest1", 
optionsStringA);
+        AMQDestination dest2 = createDestinationWithOptions("testDest2", 
optionsStringB);
+        AMQDestination dest3 = createDestinationWithOptions("testDest3", 
optionsStringA);
+        AMQDestination dest4 = createDestinationWithOptions("testDest4", 
optionsStringB);
+
+        // Destinations 1 and 3 should refer to the same address options map
+        assertSame("Destinations 1 and 3 were created with the same options 
and should refer to the same options map.",
+                dest1.getAddress().getOptions(), 
dest3.getAddress().getOptions());
+        // Destinations 2 and 4 should refer to the same address options map
+        assertSame("Destinations 2 and 4 were created with the same options 
and should refer to the same options map.",
+                dest2.getAddress().getOptions(), 
dest4.getAddress().getOptions());
+        // Destinations 1 and 2 should have a different options map
+        assertNotSame("Destinations 1 and 2 should not have the same options 
map.",
+                dest1.getAddress().getOptions(), 
dest2.getAddress().getOptions());
+
+        // Verify the contents of the shared map are as expected
+        Map<String, Object> optionsA = new HashMap<String, Object>();
+        optionsA.put("create", "always");
+        optionsA.put("node", Collections.singletonMap("type", "topic"));
+        assertEquals("Contents of the shared address options map are not as 
expected.",
+                optionsA, dest1.getAddress().getOptions());
+        assertEquals("Contents of the empty shared address options map are not 
as expected.",
+                Collections.emptyMap(), dest2.getAddress().getOptions());
+
+        // Verify that address options map is immutable
+        try
+        {
+            dest1.getAddress().getOptions().put("testKey", "testValue");
+            fail("Should not be able able to modify an address's options 
map.");
+        }
+        catch (UnsupportedOperationException e)
+        {
+            // expected
+        }
+    }
+
+    private AMQDestination createDestinationWithOptions(String destName, 
String optionsString) throws Exception
+    {
+        String addr = "ADDR:" + destName + "; " + optionsString;
+        return new AMQAnyDestination(addr);
+    }
+
+    /**
+     * Tests that when a queue has no link subscription arguments and no link 
bindings, its Subscription
+     * arguments and its bindings list refer to constant empty collections.
+     */
+    public void testEmptyLinkBindingsAndSubscriptionArgs() throws Exception
+    {
+        // no link properties
+        assertEmptyLinkBindingsAndSubscriptionArgs(new 
AMQAnyDestination("ADDR:testQueue"));
+
+        // has link properties but no x-bindings; has link x-subscribes but no 
arguments
+        String xSubscribeAddr = "ADDR:testQueueWithXSubscribes; {link: 
{x-subscribes: {exclusive: true}}}";
+        assertEmptyLinkBindingsAndSubscriptionArgs(new 
AMQAnyDestination(xSubscribeAddr));
+    }
+
+    private void assertEmptyLinkBindingsAndSubscriptionArgs(AMQDestination 
dest) {
+        assertEquals("Default link subscription arguments should be the 
constant Collections empty map.",
+                Collections.emptyMap(), 
dest.getLink().getSubscription().getArgs());
+        assertSame("Defaultl link bindings should be the constant Collections 
empty list.",
+                Collections.emptyList(), dest.getLink().getBindings());
+    }
+
+    /**
+     * Tests that when a node has no bindings specified, its bindings list 
refers to a constant empty list,
+     * so that we are not consuming extra memory unnecessarily.
+     */
+    public void testEmptyNodeBindings() throws Exception
+    {
+        // no node properties
+        assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest1"));
+        // has node properties but no x-bindings
+        assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest2; {node: 
{type: queue}}"));
+        assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest3; {node: 
{type: topic}}"));
+    }
+
+    private void assertEmptyNodeBindings(AMQDestination dest)
+    {
+        assertSame("Empty node bindings should refer to the constant 
Collections empty list.",
+                Collections.emptyList(), dest.getNode().getBindings());
+    }
+
 }

Modified: 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java
 (original)
+++ 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java
 Thu May 23 22:27:03 2013
@@ -205,6 +205,8 @@ public class ClientProperties
     public static final String QPID_DECLARE_EXCHANGES_PROP_NAME = 
"qpid.declare_exchanges";
     public static final String VERIFY_QUEUE_ON_SEND = 
"qpid.verify_queue_on_send";
 
+    public static final String QPID_MAX_CACHED_ADDR_OPTION_STRINGS = 
"qpid.max_cached_address_option_strings";
+    public static final int DEFAULT_MAX_CACHED_ADDR_OPTION_STRINGS = 10;
 
     private ClientProperties()
     {

Modified: 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/AddressParser.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/AddressParser.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/AddressParser.java
 (original)
+++ 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/AddressParser.java
 Thu May 23 22:27:03 2013
@@ -20,12 +20,17 @@
  */
 package org.apache.qpid.messaging.util;
 
-import org.apache.qpid.messaging.Address;
-
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.messaging.Address;
 
 
 /**
@@ -58,6 +63,23 @@ public class AddressParser extends Parse
 
     private static Lexer LEXER = lxi.compile();
 
+    private static final int MAX_CACHED_ENTRIES = 
Integer.getInteger(ClientProperties.QPID_MAX_CACHED_ADDR_OPTION_STRINGS,
+                                                                     
ClientProperties.DEFAULT_MAX_CACHED_ADDR_OPTION_STRINGS);
+
+    // stores address options maps for options strings that we have 
encountered; using a synchronizedMap wrapper
+    // in case multiple threads are parsing addresses.
+    private static Map<String, Map<Object, Object>> optionsMaps =
+            Collections.synchronizedMap(
+                    new LinkedHashMap<String, Map<Object, 
Object>>(MAX_CACHED_ENTRIES +1,1.1f,true)
+            {
+                @Override
+                protected boolean 
removeEldestEntry(Map.Entry<String,Map<Object, Object>> eldest)
+                {
+                    return size() > MAX_CACHED_ENTRIES;
+                }
+
+            });
+
     public static List<Token> lex(String input)
     {
         return LEXER.lex(input);
@@ -267,11 +289,27 @@ public class AddressParser extends Parse
             subject = null;
         }
 
-        Map options;
+        Map<Object, Object> options;
         if (matches(SEMI))
         {
             eat(SEMI);
-            options = map();
+
+            // get the remaining string denoting the options and see if we've 
already encountered an address
+            // with the same options before
+            String optionsString = toks2str(remainder());
+            Map<Object,Object> storedMap = optionsMaps.get(optionsString);
+            if (storedMap == null)
+            {
+                // if these are new options, construct a new map and store it 
in the encountered collection
+                options = Collections.unmodifiableMap(map());
+                optionsMaps.put(optionsString, options);
+            }
+            else
+            {
+                // if we already have the map for these options, use the 
stored map
+                options = storedMap;
+                eat_until(EOF);
+            }
         }
         else
         {

Modified: 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/Parser.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/Parser.java?rev=1485878&r1=1485877&r2=1485878&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/Parser.java
 (original)
+++ 
qpid/trunk/qpid/java/common/src/main/java/org/apache/qpid/messaging/util/Parser.java
 Thu May 23 22:27:03 2013
@@ -74,7 +74,7 @@ class Parser
 
     List<Token> eat_until(Token.Type ... types)
     {
-        List<Token> result = new ArrayList();
+        List<Token> result = new ArrayList<Token>();
         while (!matches(types))
         {
             result.add(eat());
@@ -82,4 +82,16 @@ class Parser
         return result;
     }
 
+    /**
+     * Returns the remaining list of tokens, without eating them
+     */
+    List<Token> remainder()
+    {
+        List<Token> result = new ArrayList<Token>();
+        for (int i = idx; i < tokens.size(); i++)
+        {
+            result.add(tokens.get(i));
+        }
+        return result;
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to