You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2010/12/12 19:30:03 UTC

svn commit: r1044874 - in /tomcat/trunk: java/org/apache/catalina/session/ManagerBase.java java/org/apache/catalina/util/SessionIdGenerator.java test/org/apache/catalina/session/Benchmarks.java

Author: markt
Date: Sun Dec 12 18:30:03 2010
New Revision: 1044874

URL: http://svn.apache.org/viewvc?rev=1044874&view=rev
Log:
Re-factor session ID generation into a separate class so it can be re-used for SSO (patch for that to follow)

Added:
    tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java   (with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
    tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java

Modified: tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java?rev=1044874&r1=1044873&r2=1044874&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java Sun Dec 12 18:30:03 2010
@@ -23,9 +23,6 @@ import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Deque;
@@ -35,9 +32,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.catalina.Container;
@@ -48,6 +43,7 @@ import org.apache.catalina.Manager;
 import org.apache.catalina.Session;
 import org.apache.catalina.mbeans.MBeanUtils;
 import org.apache.catalina.util.LifecycleMBeanBase;
+import org.apache.catalina.util.SessionIdGenerator;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.res.StringManager;
@@ -109,44 +105,37 @@ public abstract class ManagerBase extend
 
 
     /**
-     * Queue of random number generator objects to be used when creating session
-     * identifiers. If the queue is empty when a random number generator is
-     * required, a new random number generator object is created. This is
-     * designed this way since random number generator use a sync to make them
-     * thread-safe and the sync makes using a a single object slow(er).
-     */
-    protected Queue<SecureRandom> randoms =
-        new ConcurrentLinkedQueue<SecureRandom>();
-
-    /**
      * The Java class name of the secure random number generator class to be
      * used when generating session identifiers. The random number generator
      * class must be self-seeding and have a zero-argument constructor. If not
-     * specified, an instance of {@link SecureRandom} will be generated.
+     * specified, an instance of {@link java.secure.SecureRandom} will be
+     * generated.
      */
     protected String secureRandomClass = null;
 
     /**
      * The name of the algorithm to use to create instances of
-     * {@link SecureRandom} which are used to generate session IDs. If no
-     * algorithm is specified, SHA1PRNG is used. To use the platform default
-     * (which may be SHA1PRNG), specify the empty string. If an invalid
-     * algorithm and/or provider is specified the {@link SecureRandom} instances
-     * will be created using the defaults. If that fails, the {@link
-     * SecureRandom} instances will be created using platform defaults.
+     * {@link java.secure.SecureRandom} which are used to generate session IDs.
+     * If no algorithm is specified, SHA1PRNG is used. To use the platform
+     * default (which may be SHA1PRNG), specify the empty string. If an invalid
+     * algorithm and/or provider is specified the SecureRandom instances will be
+     * created using the defaults. If that fails, the SecureRandom instances
+     * will be created using platform defaults.
      */
     protected String secureRandomAlgorithm = "SHA1PRNG";
 
     /**
      * The name of the provider to use to create instances of
-     * {@link SecureRandom} which are used to generate session IDs. If
-     * no algorithm is specified the of SHA1PRNG default is used. If an invalid
-     * algorithm and/or provider is specified the {@link SecureRandom} instances
-     * will be created using the defaults. If that fails, the {@link
-     * SecureRandom} instances will be created using platform defaults.
+     * {@link java.secure.SecureRandom} which are used to generate session IDs. 
+     * If no algorithm is specified the of SHA1PRNG default is used. If an
+     * invalid algorithm and/or provider is specified the SecureRandom instances
+     * will be created using the defaults. If that fails, the SecureRandom
+     * instances will be created using platform defaults.
      */
     protected String secureRandomProvider = null;
-    
+
+    protected SessionIdGenerator sessionIdGenerator = null;
+
     /**
      * The longest time (in seconds) that an expired session had been alive.
      */
@@ -386,71 +375,6 @@ public abstract class ManagerBase extend
     }
 
     /**
-     * Create a new random number generator instance we should use for
-     * generating session identifiers.
-     */
-    protected SecureRandom createSecureRandom() {
-
-        SecureRandom result = null;
-        
-        long t1 = System.currentTimeMillis();
-        if (secureRandomClass != null) {
-            try {
-                // Construct and seed a new random number generator
-                Class<?> clazz = Class.forName(secureRandomClass);
-                result = (SecureRandom) clazz.newInstance();
-            } catch (Exception e) {
-                log.error(sm.getString("managerBase.random",
-                        secureRandomClass), e);
-            }
-        }
-
-        if (result == null) {
-            // No secureRandomClass or creation failed. Use SecureRandom.
-            try {
-                if (secureRandomProvider != null &&
-                        secureRandomProvider.length() > 0) {
-                    result = SecureRandom.getInstance(secureRandomAlgorithm,
-                            secureRandomProvider);
-                } else if (secureRandomAlgorithm != null &&
-                        secureRandomAlgorithm.length() > 0) {
-                    result = SecureRandom.getInstance(secureRandomAlgorithm);
-                }
-            } catch (NoSuchAlgorithmException e) {
-                log.error(sm.getString("managerBase.randomAlgorithm",
-                        secureRandomAlgorithm), e);
-            } catch (NoSuchProviderException e) {
-                log.error(sm.getString("managerBase.randomProvider",
-                        secureRandomProvider), e);
-            }
-        }
-
-        if (result == null) {
-            // Invalid provider / algorithm
-            try {
-                result = SecureRandom.getInstance("SHA1PRNG");
-            } catch (NoSuchAlgorithmException e) {
-                log.error(sm.getString("managerBase.randomAlgorithm",
-                        secureRandomAlgorithm), e);
-            }
-        }
-        
-        if (result == null) {
-            // Nothing works - use platform default
-            result = new SecureRandom();
-        }
-
-        if(log.isDebugEnabled()) {
-            long t2=System.currentTimeMillis();
-            if( (t2-t1) > 100 )
-                log.debug(sm.getString("managerBase.createRandom",
-                        Long.valueOf(t2-t1)));
-        }
-        return result;
-    }
-
-
-    /**
      * Return the secure random number generator class name.
      */
     public String getSecureRandomClass() {
@@ -637,17 +561,24 @@ public abstract class ManagerBase extend
             sessionExpirationTiming.add(null);
         }
 
+        sessionIdGenerator = new SessionIdGenerator();
+        sessionIdGenerator.setJvmRoute(getJvmRoute());
+        sessionIdGenerator.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
+        sessionIdGenerator.setSecureRandomClass(getSecureRandomClass());
+        sessionIdGenerator.setSecureRandomProvider(getSecureRandomProvider());
+        sessionIdGenerator.setSessionIdLength(getSessionIdLength());
+
         // Force initialization of the random number generator
         if (log.isDebugEnabled())
             log.debug("Force random number initialization starting");
-        generateSessionId();
+        sessionIdGenerator.generateSessionId();
         if (log.isDebugEnabled())
             log.debug("Force random number initialization completed");
     }
 
     @Override
     protected void stopInternal() throws LifecycleException {
-        this.randoms.clear();
+        this.sessionIdGenerator = null;
     }
 
 
@@ -852,60 +783,23 @@ public abstract class ManagerBase extend
     }
 
 
-    protected void getRandomBytes(byte bytes[]) {
-
-        SecureRandom random = randoms.poll();
-        if (random == null) {
-            random = createSecureRandom();
-        }
-        random.nextBytes(bytes);
-        randoms.add(random);
-    }
-
-
     /**
      * Generate and return a new session identifier.
      */
     protected String generateSessionId() {
 
-        byte random[] = new byte[16];
-        String jvmRoute = getJvmRoute();
         String result = null;
 
-        // Render the result as a String of hexadecimal digits
-        StringBuilder buffer = new StringBuilder();
         do {
-            int resultLenBytes = 0;
             if (result != null) {
-                buffer = new StringBuilder();
                 duplicates++;
             }
 
-            while (resultLenBytes < this.sessionIdLength) {
-                getRandomBytes(random);
-                for (int j = 0;
-                j < random.length && resultLenBytes < this.sessionIdLength;
-                j++) {
-                    byte b1 = (byte) ((random[j] & 0xf0) >> 4);
-                    byte b2 = (byte) (random[j] & 0x0f);
-                    if (b1 < 10)
-                        buffer.append((char) ('0' + b1));
-                    else
-                        buffer.append((char) ('A' + (b1 - 10)));
-                    if (b2 < 10)
-                        buffer.append((char) ('0' + b2));
-                    else
-                        buffer.append((char) ('A' + (b2 - 10)));
-                    resultLenBytes++;
-                }
-            }
-            if (jvmRoute != null) {
-                buffer.append('.').append(jvmRoute);
-            }
-            result = buffer.toString();
+            result = sessionIdGenerator.generateSessionId();
+            
         } while (sessions.containsKey(result));
-        return (result);
-
+        
+        return result;
     }
 
 

Added: tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java?rev=1044874&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java (added)
+++ tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java Sun Dec 12 18:30:03 2010
@@ -0,0 +1,253 @@
+/*
+ * 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.catalina.util;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class SessionIdGenerator {
+
+    private static Log log = LogFactory.getLog(SessionIdGenerator.class);
+
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.catalina.util");
+
+
+    /**
+     * Queue of random number generator objects to be used when creating session
+     * identifiers. If the queue is empty when a random number generator is
+     * required, a new random number generator object is created. This is
+     * designed this way since random number generators use a sync to make them
+     * thread-safe and the sync makes using a a single object slow(er).
+     */
+    private Queue<SecureRandom> randoms =
+        new ConcurrentLinkedQueue<SecureRandom>();
+
+
+    /**
+     * The Java class name of the secure random number generator class to be
+     * used when generating session identifiers. The random number generator
+     * class must be self-seeding and have a zero-argument constructor. If not
+     * specified, an instance of {@link SecureRandom} will be generated.
+     */
+    private String secureRandomClass = null;
+
+
+    /**
+     * The name of the algorithm to use to create instances of
+     * {@link SecureRandom} which are used to generate session IDs. If no
+     * algorithm is specified, SHA1PRNG is used. To use the platform default
+     * (which may be SHA1PRNG), specify the empty string. If an invalid
+     * algorithm and/or provider is specified the {@link SecureRandom} instances
+     * will be created using the defaults. If that fails, the {@link
+     * SecureRandom} instances will be created using platform defaults.
+     */
+    private String secureRandomAlgorithm = "SHA1PRNG";
+
+
+    /**
+     * The name of the provider to use to create instances of
+     * {@link SecureRandom} which are used to generate session IDs. If
+     * no algorithm is specified the of SHA1PRNG default is used. If an invalid
+     * algorithm and/or provider is specified the {@link SecureRandom} instances
+     * will be created using the defaults. If that fails, the {@link
+     * SecureRandom} instances will be created using platform defaults.
+     */
+    private String secureRandomProvider = null;
+
+
+    /** Node identifier when in a cluster. Defaults to the empty string. */
+    private String jvmRoute = "";
+
+
+    /** Number of bytes in a session ID. Defaults to 16. */
+    private int sessionIdLength = 16;
+
+
+    /**
+     * Specify a non-default @{link {@link SecureRandom} implementation to use.
+     * 
+     * @param secureRandomClass The fully-qualified class name
+     */
+    public void setSecureRandomClass(String secureRandomClass) {
+        this.secureRandomClass = secureRandomClass;
+    }
+
+
+    /**
+     * Specify a non-default algorithm to use to generate random numbers.
+     * 
+     * @param secureRandomAlgorithm The name of the algorithm
+     */
+    public void setSecureRandomAlgorithm(String secureRandomAlgorithm) {
+        this.secureRandomAlgorithm = secureRandomAlgorithm;
+    }
+
+
+    /**
+     * Specify a non-default provider to use to generate random numbers.
+     * 
+     * @param secureRandomProvider  The name of the provider
+     */
+    public void setSecureRandomProvider(String secureRandomProvider) {
+        this.secureRandomProvider = secureRandomProvider;
+    }
+
+
+    /**
+     * Specify the node identifier associated with this node which will be
+     * included in the generated session ID.
+     * 
+     * @param jvmRoute  The node identifier
+     */
+    public void setJvmRoute(String jvmRoute) {
+        this.jvmRoute = jvmRoute;
+    }
+
+
+    /**
+     * Specify the number of bytes for a session ID
+     * 
+     * @param sessionIdLength   Number of bytes
+     */
+    public void setSessionIdLength(int sessionIdLength) {
+        this.sessionIdLength = sessionIdLength;
+    }
+
+
+    /**
+     * Generate and return a new session identifier.
+     */
+    public String generateSessionId() {
+
+        byte random[] = new byte[16];
+
+        // Render the result as a String of hexadecimal digits
+        StringBuilder buffer = new StringBuilder();
+
+        int resultLenBytes = 0;
+
+        while (resultLenBytes < sessionIdLength) {
+            getRandomBytes(random);
+            for (int j = 0;
+            j < random.length && resultLenBytes < sessionIdLength;
+            j++) {
+                byte b1 = (byte) ((random[j] & 0xf0) >> 4);
+                byte b2 = (byte) (random[j] & 0x0f);
+                if (b1 < 10)
+                    buffer.append((char) ('0' + b1));
+                else
+                    buffer.append((char) ('A' + (b1 - 10)));
+                if (b2 < 10)
+                    buffer.append((char) ('0' + b2));
+                else
+                    buffer.append((char) ('A' + (b2 - 10)));
+                resultLenBytes++;
+            }
+        }
+
+        if (jvmRoute != null) {
+            buffer.append('.').append(jvmRoute);
+        }
+
+        return buffer.toString();
+    }
+    
+    
+    private void getRandomBytes(byte bytes[]) {
+
+        SecureRandom random = randoms.poll();
+        if (random == null) {
+            random = createSecureRandom();
+        }
+        random.nextBytes(bytes);
+        randoms.add(random);
+    }
+    
+    
+    /**
+     * Create a new random number generator instance we should use for
+     * generating session identifiers.
+     */
+    private SecureRandom createSecureRandom() {
+
+        SecureRandom result = null;
+        
+        long t1 = System.currentTimeMillis();
+        if (secureRandomClass != null) {
+            try {
+                // Construct and seed a new random number generator
+                Class<?> clazz = Class.forName(secureRandomClass);
+                result = (SecureRandom) clazz.newInstance();
+            } catch (Exception e) {
+                log.error(sm.getString("managerBase.random",
+                        secureRandomClass), e);
+            }
+        }
+
+        if (result == null) {
+            // No secureRandomClass or creation failed. Use SecureRandom.
+            try {
+                if (secureRandomProvider != null &&
+                        secureRandomProvider.length() > 0) {
+                    result = SecureRandom.getInstance(secureRandomAlgorithm,
+                            secureRandomProvider);
+                } else if (secureRandomAlgorithm != null &&
+                        secureRandomAlgorithm.length() > 0) {
+                    result = SecureRandom.getInstance(secureRandomAlgorithm);
+                }
+            } catch (NoSuchAlgorithmException e) {
+                log.error(sm.getString("managerBase.randomAlgorithm",
+                        secureRandomAlgorithm), e);
+            } catch (NoSuchProviderException e) {
+                log.error(sm.getString("managerBase.randomProvider",
+                        secureRandomProvider), e);
+            }
+        }
+
+        if (result == null) {
+            // Invalid provider / algorithm
+            try {
+                result = SecureRandom.getInstance("SHA1PRNG");
+            } catch (NoSuchAlgorithmException e) {
+                log.error(sm.getString("managerBase.randomAlgorithm",
+                        secureRandomAlgorithm), e);
+            }
+        }
+        
+        if (result == null) {
+            // Nothing works - use platform default
+            result = new SecureRandom();
+        }
+
+        if(log.isDebugEnabled()) {
+            long t2=System.currentTimeMillis();
+            if( (t2-t1) > 100 )
+                log.debug(sm.getString("managerBase.createRandom",
+                        Long.valueOf(t2-t1)));
+        }
+        return result;
+    }
+}

Propchange: tomcat/trunk/java/org/apache/catalina/util/SessionIdGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java?rev=1044874&r1=1044873&r2=1044874&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java (original)
+++ tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java Sun Dec 12 18:30:03 2010
@@ -24,6 +24,7 @@ import java.security.SecureRandom;
 
 import junit.framework.TestCase;
 
+import org.apache.catalina.LifecycleException;
 import org.apache.catalina.Session;
 
 import org.apache.catalina.core.StandardContext;
@@ -70,6 +71,7 @@ public class Benchmarks extends TestCase
 
         // Create a default session manager
         StandardManager mgr = new StandardManager();
+        mgr.startInternal();
         mgr.generateSessionId();
         while (mgr.sessionCreationTiming.size() <
                 ManagerBase.TIMING_STATS_CACHE_SIZE) {
@@ -108,8 +110,6 @@ public class Benchmarks extends TestCase
         result.append(threadCount);
         result.append(", Time(ms): ");
         result.append(end-start);
-        result.append(", Randoms: ");
-        result.append(mgr.randoms.size());
         System.out.println(result.toString());
     }
     
@@ -146,7 +146,7 @@ public class Benchmarks extends TestCase
      *  4 threads - ~11,700ms
      * 16 threads - ~45,600ms
      */
-    public void testManagerBaseCreateSession() {
+    public void testManagerBaseCreateSession() throws LifecycleException {
         doTestManagerBaseCreateSession(1, 1000000);
         doTestManagerBaseCreateSession(2, 1000000);
         doTestManagerBaseCreateSession(4, 1000000);
@@ -157,10 +157,12 @@ public class Benchmarks extends TestCase
     }
     
     
-    private void doTestManagerBaseCreateSession(int threadCount, int iterCount) {
+    private void doTestManagerBaseCreateSession(int threadCount, int iterCount)
+            throws LifecycleException {
 
         // Create a default session manager
         StandardManager mgr = new StandardManager();
+        mgr.startInternal();
         mgr.setContainer(new StandardContext());
         mgr.generateSessionId();
         while (mgr.sessionCreationTiming.size() <
@@ -199,8 +201,6 @@ public class Benchmarks extends TestCase
         result.append(threadCount);
         result.append(", Time(ms): ");
         result.append(end-start);
-        result.append(", Randoms: ");
-        result.append(mgr.randoms.size());
         System.out.println(result.toString());
     }
     
@@ -331,8 +331,12 @@ public class Benchmarks extends TestCase
         @Override
         public void run() {
             try {
+                int read = 0;
                 for (int i = 0; i < count; i++) {
-                    is.read(bytes);
+                    read = is.read(bytes);
+                    if (read < bytes.length) {
+                        throw new IOException("Only read " + read + " bytes");
+                    }
                 }
             } catch (IOException e) {
                 e.printStackTrace();



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org