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