You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by ve...@apache.org on 2012/12/20 16:40:36 UTC

svn commit: r1424536 - in /webservices/commons/trunk/modules/axiom/modules/axiom-api/src: main/java/org/apache/axiom/util/UIDGenerator.java main/java/org/apache/axiom/util/UUIDCache.java test/java/org/apache/axiom/util/UIDGeneratorTest.java

Author: veithen
Date: Thu Dec 20 15:40:36 2012
New Revision: 1424536

URL: http://svn.apache.org/viewvc?rev=1424536&view=rev
Log:
Improved the performance of the UIDGenerator#generateURNString() method.

Added:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java   (with props)
Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java?rev=1424536&r1=1424535&r2=1424536&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java Thu Dec 20 15:40:36 2012
@@ -21,6 +21,7 @@ package org.apache.axiom.util;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.security.SecureRandom;
 import java.util.Random;
 import java.util.UUID;
 
@@ -28,7 +29,8 @@ import java.util.UUID;
  * Contains utility methods to generate unique IDs of various kinds.
  * <p>
  * Depending on the requested type of ID, this class will either use
- * {@link UUID#randomUUID()} or its own unique ID generator. This implementation
+ * {@link UUID#randomUUID()} (or an equivalent algorithm)
+ * or its own unique ID generator. This implementation
  * generates unique IDs based on the assumption that the following triplet is
  * unique:
  * <ol>
@@ -67,11 +69,25 @@ public final class UIDGenerator {
     private static final long threadIdXorOperand;
     private static final long seqXorOperand;
     
+    private static final SecureRandom secureRandom = new SecureRandom();
+    
+    /**
+     * Array of 16 caches that contain random bytes fetched from {@link #secureRandom} and that are
+     * used to compute UUIDs. These caches are used to reduce the number of calls to
+     * {@link SecureRandom#nextBytes(byte[])}. The cache used by a given thread is determined by the
+     * thread ID. Multiple caches are used to reduce contention between threads.
+     */
+    private static final UUIDCache[] uuidCaches;
+    
     static {
         Random rand = new Random();
         threadIdXorOperand = rand.nextLong();
         startTimeXorOperand = rand.nextLong();
         seqXorOperand = rand.nextLong();
+        uuidCaches = new UUIDCache[16];
+        for (int i=0; i<16; i++) {
+            uuidCaches[i] = new UUIDCache();
+        }
     }
     
     /**
@@ -93,10 +109,14 @@ public final class UIDGenerator {
     private static void writeReverseLongHex(long value, StringBuilder buffer) {
         for (int i=0; i<16; i++) {
             int n = (int)(value >> (4*i)) & 0xF;
-            buffer.append((char)(n < 10 ? '0' + n : 'a' + n - 10));
+            writeNibble(n, buffer);
         }
     }
     
+    private static void writeNibble(int n, StringBuilder buffer) {
+        buffer.append((char)(n < 10 ? '0' + n : 'a' + n - 10));
+    }
+    
     /**
      * Generate a unique ID as hex value and add it to the given buffer. Note
      * that with respect to the triplet, the order of nibbles is reversed, i.e.
@@ -210,7 +230,53 @@ public final class UIDGenerator {
      * @return the generated URN
      */
     public static String generateURNString() {
-        return "urn:uuid:" + UUID.randomUUID();
+        StringBuilder urn = new StringBuilder(45);
+        urn.append("urn:uuid:");
+        UUIDCache cache = uuidCaches[(int)Thread.currentThread().getId() & 0xF];
+        synchronized (cache) {
+            boolean fill;
+            int position = cache.position;
+            byte[] randomBytes = cache.randomBytes;
+            if (randomBytes == null) {
+                cache.randomBytes = randomBytes = new byte[4096];
+                fill = true;
+            } else if (position == 4096) {
+                position = 0;
+                fill = true;
+            } else {
+                fill = false;
+            }
+            if (fill) {
+                secureRandom.nextBytes(cache.randomBytes);
+            }
+            writeHex(randomBytes[position], urn);
+            writeHex(randomBytes[position+1], urn);
+            writeHex(randomBytes[position+2], urn);
+            writeHex(randomBytes[position+3], urn);
+            urn.append('-');
+            writeHex(randomBytes[position+4], urn);
+            writeHex(randomBytes[position+5], urn);
+            urn.append('-');
+            writeHex((byte)(randomBytes[position+6] & 0x0F | 0x40), urn);
+            writeHex(randomBytes[position+7], urn);
+            urn.append('-');
+            writeHex((byte)(randomBytes[position+8] & 0x3F | 0x80), urn);
+            writeHex(randomBytes[position+9], urn);
+            urn.append('-');
+            writeHex(randomBytes[position+10], urn);
+            writeHex(randomBytes[position+11], urn);
+            writeHex(randomBytes[position+12], urn);
+            writeHex(randomBytes[position+13], urn);
+            writeHex(randomBytes[position+14], urn);
+            writeHex(randomBytes[position+15], urn);
+            cache.position = position+16;
+        }
+        return urn.toString();
+    }
+    
+    private static void writeHex(byte b, StringBuilder buffer) {
+        writeNibble(b >> 4 & 0xF, buffer);
+        writeNibble(b & 0xF, buffer);
     }
     
     /**

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java?rev=1424536&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java Thu Dec 20 15:40:36 2012
@@ -0,0 +1,24 @@
+/*
+ * 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.axiom.util;
+
+final class UUIDCache {
+    int position;
+    byte[] randomBytes;
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UUIDCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java?rev=1424536&r1=1424535&r2=1424536&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java Thu Dec 20 15:40:36 2012
@@ -22,6 +22,7 @@ package org.apache.axiom.util;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
@@ -45,7 +46,7 @@ public class UIDGeneratorTest extends Te
         assertTrue(UIDGenerator.generateMimeBoundary().length() <= 70);
     }
     
-    public void testThreadSafety() {
+    public void testGenerateUIDThreadSafety() {
         final Set generatedIds = Collections.synchronizedSet(new HashSet());
         final AtomicInteger errorCount = new AtomicInteger(0);
         Thread[] threads = new Thread[100];
@@ -74,4 +75,40 @@ public class UIDGeneratorTest extends Te
         
         assertEquals(0, errorCount.get());
     }
+    
+    public void testGenerateURNString() {
+        Thread[] threads = new Thread[100];
+        final String[][] urns = new String[threads.length][1000];
+        for (int i = 0; i < threads.length; i++) {
+            final String[] threadURNs = urns[i];
+            threads[i] = new Thread(new Runnable() {
+                public void run() {
+                    for (int i=0; i<threadURNs.length; i++) {
+                        threadURNs[i] = UIDGenerator.generateURNString();
+                    }
+                }
+            });
+            threads[i].start();
+        }
+
+        for (int i = 0; i < threads.length; i++) {
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        Set set = new HashSet();
+        for (int i = 0; i < threads.length; i++) {
+            for (int j = 0; j < urns[i].length; j++) {
+                String urn = urns[i][j];
+                assertTrue(urn.startsWith("urn:uuid:"));
+                assertTrue(set.add(urn));
+                UUID uuid = UUID.fromString(urn.substring(9));
+                assertEquals(4, uuid.version());
+                assertEquals(2, uuid.variant());
+            }
+        }
+    }
 }