Merge branch 'master' into javawebsocket-jsr
This commit is contained in:
commit
8ec9ac4d64
68
VERSION.txt
68
VERSION.txt
|
@ -39,6 +39,74 @@ jetty-9.0.0.v20130308 - 08 March 2013
|
|||
+ 402757 WebSocket client module can't be used with WebSocket server
|
||||
module in the same WAR
|
||||
|
||||
jetty-8.1.10.v20130312 - 12 March 2013
|
||||
+ 376273 Early EOF because of SSL Protocol Error on
|
||||
https://api-3t.paypal.com/nvp.
|
||||
+ 381521 allow compress methods to be configured
|
||||
+ 392129 fixed handling of timeouts after startAsync
|
||||
+ 394064 ensure that JarFile instances are closed on JarFileResource.release()
|
||||
+ 398649 ServletContextListener.contextDestroyed() is not called on
|
||||
ContextHandler unregistration
|
||||
+ 399703 made encoding error handling consistent
|
||||
+ 399799 do not hold lock while calling invalidation listeners
|
||||
+ 399967 Shutdown hook calls destroy
|
||||
+ 400040 NullPointerException in HttpGenerator.prepareBuffers
|
||||
+ 400142 ConcurrentModificationException in JDBC SessionManger
|
||||
+ 400144 When loading a session fails the JDBCSessionManger produces duplicate
|
||||
session IDs
|
||||
+ 400312 ServletContextListener.contextInitialized() is not called when added
|
||||
in ServletContainerInitializer.onStartup
|
||||
+ 400457 Thread context classloader hierarchy not searched when finding
|
||||
webapp's java:comp/env
|
||||
+ 400859 limit max size of writes from cached content
|
||||
+ 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib
|
||||
+ 401317 Make Safari 5.x websocket support minVersion level error more clear
|
||||
+ 401382 Prevent parseAvailable from parsing next chunk when previous has not
|
||||
been consumed. Handle no content-type in chunked request.
|
||||
+ 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser
|
||||
+ 401485 zip file closed exception
|
||||
+ 401531 StringIndexOutOfBoundsException for "/*" <url-pattern> of
|
||||
<jsp-property-group> fix for multiple mappings to *.jsp
|
||||
+ 401908 Enhance DosFilter to allow dynamic configuration of attributes.
|
||||
+ 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty
|
||||
server is stopped
|
||||
+ 402485 reseed secure random
|
||||
+ 402735 jetty.sh to support status which is == check
|
||||
+ 402833 Test harness for global error page and hide exception message from
|
||||
reason string
|
||||
|
||||
jetty-7.6.10.v20130312 - 12 March 2013
|
||||
+ 376273 Early EOF because of SSL Protocol Error on
|
||||
https://api-3t.paypal.com/nvp.
|
||||
+ 381521 allow compress methods to be configured
|
||||
+ 394064 ensure that JarFile instances are closed on JarFileResource.release()
|
||||
+ 398649 ServletContextListener.contextDestroyed() is not called on
|
||||
ContextHandler unregistration
|
||||
+ 399703 made encoding error handling consistent
|
||||
+ 399799 do not hold lock while calling invalidation listeners
|
||||
+ 399967 Shutdown hook calls destroy
|
||||
+ 400040 NullPointerException in HttpGenerator.prepareBuffers
|
||||
+ 400142 ConcurrentModificationException in JDBC SessionManger
|
||||
+ 400144 When loading a session fails the JDBCSessionManger produces duplicate
|
||||
session IDs
|
||||
+ 400457 Thread context classloader hierarchy not searched when finding
|
||||
webapp's java:comp/env
|
||||
+ 400859 limit max size of writes from cached content
|
||||
+ 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib
|
||||
+ 401317 Make Safari 5.x websocket support minVersion level error more clear
|
||||
+ 401382 Prevent parseAvailable from parsing next chunk when previous has not
|
||||
been consumed. Handle no content-type in chunked request.
|
||||
+ 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser
|
||||
+ 401531 StringIndexOutOfBoundsException for "/*" <url-pattern> of
|
||||
<jsp-property-group> fix for multiple mappings to *.jsp
|
||||
+ 401908 Enhance DosFilter to allow dynamic configuration of attributes.
|
||||
+ 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty
|
||||
server is stopped
|
||||
+ 402485 reseed secure random
|
||||
+ 402735 jetty.sh to support status which is == check
|
||||
+ 402833 Test harness for global error page and hide exception message from
|
||||
reason string
|
||||
|
||||
jetty-9.0.0.RC2 - 24 February 2013
|
||||
+ Fix etc/jetty.xml TimerScheduler typo that is preventing normal startup
|
||||
+ Fix etc/jetty-https.xml ExcludeCipherSuites typo that prevents SSL startup
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.deploy.providers.WebAppProvider;
|
|||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||
import org.eclipse.jetty.server.AsyncNCSARequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
|
@ -152,7 +153,8 @@ public class SpdyServer
|
|||
login.setConfig(jetty_home + "/etc/realm.properties");
|
||||
server.addBean(login);
|
||||
|
||||
NCSARequestLog requestLog = new NCSARequestLog(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
NCSARequestLog requestLog = new AsyncNCSARequestLog();
|
||||
requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
requestLog.setExtended(false);
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ public class HostnameVerificationTest
|
|||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
}
|
||||
sslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
|
||||
if (server == null)
|
||||
server = new Server();
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<assembly-directory>target/distribution</assembly-directory>
|
||||
<jetty-setuid-version>1.0.0</jetty-setuid-version>
|
||||
<jetty-setuid-version>1.0.1</jetty-setuid-version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
|
@ -161,6 +161,8 @@
|
|||
<outputDirectory>${assembly-directory}/lib/setuid</outputDirectory>
|
||||
<destFileName>libsetuid-linux.so</destFileName>
|
||||
</artifactItem>
|
||||
|
||||
<!-- TODO readd once new version released
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty.toolchain.setuid</groupId>
|
||||
<artifactId>libsetuid-osx</artifactId>
|
||||
|
@ -170,6 +172,7 @@
|
|||
<outputDirectory>${assembly-directory}/lib/setuid</outputDirectory>
|
||||
<destFileName>libsetuid-osx.so</destFileName>
|
||||
</artifactItem>
|
||||
-->
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -106,10 +106,28 @@ findDirectory()
|
|||
|
||||
running()
|
||||
{
|
||||
local PID=$(cat "$1" 2>/dev/null) || return 1
|
||||
local PID=$(head -n 1 "$1" 2>/dev/null) || return 1
|
||||
kill -0 "$PID" 2>/dev/null
|
||||
}
|
||||
|
||||
started()
|
||||
{
|
||||
# wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument
|
||||
for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
|
||||
do
|
||||
sleep 4
|
||||
[ -z "$(grep STARTED $1)" ] || return 0
|
||||
[ -z "$(grep STOPPED $1)" ] || return 1
|
||||
[ -z "$(grep FAILED $1)" ] || return 1
|
||||
local PID=$(head -n 1 "$1" 2>/dev/null) || return 1
|
||||
kill -0 "$PID" 2>/dev/null || return 1
|
||||
echo -n ". "
|
||||
done
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
readConfig()
|
||||
{
|
||||
(( DEBUG )) && echo "Reading $1.."
|
||||
|
@ -137,7 +155,13 @@ shift
|
|||
##################################################
|
||||
# Read any configuration files
|
||||
##################################################
|
||||
for CONFIG in /etc/default/jetty{,9} $HOME/.jettyrc; do
|
||||
ETC=/etc
|
||||
if [ $UID != 0 ]
|
||||
then
|
||||
ETC=$HOME/etc
|
||||
fi
|
||||
|
||||
for CONFIG in $ETC/default/jetty{,9} $HOME/.jettyrc; do
|
||||
if [ -f "$CONFIG" ] ; then
|
||||
readConfig "$CONFIG"
|
||||
fi
|
||||
|
@ -262,9 +286,9 @@ fi
|
|||
##################################################
|
||||
if [ -z "$JETTY_CONF" ]
|
||||
then
|
||||
if [ -f /etc/jetty.conf ]
|
||||
if [ -f $ETC/jetty.conf ]
|
||||
then
|
||||
JETTY_CONF=/etc/jetty.conf
|
||||
JETTY_CONF=$ETC/jetty.conf
|
||||
elif [ -f "$JETTY_HOME/etc/jetty.conf" ]
|
||||
then
|
||||
JETTY_CONF=$JETTY_HOME/etc/jetty.conf
|
||||
|
@ -318,6 +342,7 @@ if [ -z "$JETTY_PID" ]
|
|||
then
|
||||
JETTY_PID="$JETTY_RUN/jetty.pid"
|
||||
fi
|
||||
JAVA_OPTIONS+=("-Djetty.pid=$JETTY_PID")
|
||||
|
||||
##################################################
|
||||
# Setup JAVA if unset
|
||||
|
@ -407,23 +432,15 @@ case "$ACTION" in
|
|||
exit
|
||||
fi
|
||||
|
||||
if type start-stop-daemon > /dev/null 2>&1
|
||||
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
|
||||
then
|
||||
unset CH_USER
|
||||
if [ -n "$JETTY_USER" ]
|
||||
then
|
||||
CH_USER="-c$JETTY_USER"
|
||||
fi
|
||||
if start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_HOME" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" --daemon
|
||||
then
|
||||
sleep 1
|
||||
if running "$JETTY_PID"
|
||||
then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
fi
|
||||
|
||||
start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_HOME" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" --daemon
|
||||
|
||||
else
|
||||
|
||||
|
@ -454,14 +471,21 @@ case "$ACTION" in
|
|||
echo $! > "$JETTY_PID"
|
||||
fi
|
||||
|
||||
echo "STARTED Jetty `date`"
|
||||
fi
|
||||
|
||||
if started "$JETTY_PID"
|
||||
then
|
||||
echo "OK `date`"
|
||||
else
|
||||
echo "FAILED `date`"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo -n "Stopping Jetty: "
|
||||
if type start-stop-daemon > /dev/null 2>&1; then
|
||||
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then
|
||||
start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP
|
||||
|
||||
TIMEOUT=30
|
||||
|
@ -476,7 +500,7 @@ case "$ACTION" in
|
|||
rm -f "$JETTY_PID"
|
||||
echo OK
|
||||
else
|
||||
PID=$(cat "$JETTY_PID" 2>/dev/null)
|
||||
PID=$(head -n 1 "$JETTY_PID" 2>/dev/null)
|
||||
kill "$PID" 2>/dev/null
|
||||
|
||||
TIMEOUT=30
|
||||
|
@ -537,7 +561,7 @@ case "$ACTION" in
|
|||
|
||||
;;
|
||||
|
||||
check)
|
||||
check|status)
|
||||
echo "Checking arguments to Jetty: "
|
||||
echo "JETTY_HOME = $JETTY_HOME"
|
||||
echo "JETTY_CONF = $JETTY_CONF"
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Mixin the Start FileNoticeLifeCycleListener -->
|
||||
<!-- =============================================================== -->
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
<Call name="addLifeCycleListener">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.util.component.FileNoticeLifeCycleListener">
|
||||
<Arg><SystemProperty name="jetty.pid" default="./jetty.pid"/></Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -6,8 +6,7 @@
|
|||
# created by that script.
|
||||
#
|
||||
# Each line in this file becomes an arguement to start.jar
|
||||
# unless this file contains an --ini option, then these
|
||||
# arguments will be in addition to those found in the
|
||||
# start.ini file
|
||||
# in addition to those found in the start.ini file
|
||||
# =======================================================
|
||||
--pre=etc/jetty-logging.xml
|
||||
etc/jetty-logging.xml
|
||||
etc/jetty-started.xml
|
||||
|
|
|
@ -83,17 +83,6 @@
|
|||
# -XX:CMSInitiatingOccupancyFraction=80
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# Enable SetUID
|
||||
# To enable setuid you must have the jetty-setuid.xml as the
|
||||
# first xml file to be processed.
|
||||
# The default user and group is 'jetty' and if you are
|
||||
# starting as root you must change the run privledged to true
|
||||
#-----------------------------------------------------------
|
||||
# OPTIONS=setuid
|
||||
# etc/jetty-setuid.xml
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# Default Server Options
|
||||
# Use the core server jars with websocket on the classpath
|
||||
|
@ -107,6 +96,19 @@ etc/jetty.xml
|
|||
start.d/
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# Enable SetUID
|
||||
# The default user and group is 'jetty' and if you are
|
||||
# starting as root you must change the run privledged to true
|
||||
#-----------------------------------------------------------
|
||||
# OPTIONS=setuid
|
||||
# etc/jetty-setuid.xml
|
||||
# jetty.startServerAsPrivileged=false
|
||||
# jetty.username=jetty
|
||||
# jetty.groupname=jetty
|
||||
# jetty.umask=002
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# Server logging.
|
||||
# The following configuration will redirect stderr and stdout
|
||||
|
|
|
@ -112,16 +112,14 @@ public interface HttpContent
|
|||
@Override
|
||||
public ByteBuffer getIndirectBuffer()
|
||||
{
|
||||
try
|
||||
if (_resource.length()<=0 || _maxBuffer<_resource.length())
|
||||
return null;
|
||||
int length=(int)_resource.length();
|
||||
byte[] array = new byte[length];
|
||||
|
||||
int offset=0;
|
||||
try (InputStream in=_resource.getInputStream())
|
||||
{
|
||||
if (_resource.length()<=0 || _maxBuffer<_resource.length())
|
||||
return null;
|
||||
int length=(int)_resource.length();
|
||||
byte[] array = new byte[length];
|
||||
|
||||
int offset=0;
|
||||
InputStream in=_resource.getInputStream();
|
||||
|
||||
do
|
||||
{
|
||||
int filled=in.read(array,offset,length);
|
||||
|
|
|
@ -26,16 +26,13 @@ import org.eclipse.jetty.util.log.Log;
|
|||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** An Abstract implementation of an Idle Timeout.
|
||||
*
|
||||
/**
|
||||
* An Abstract implementation of an Idle Timeout.
|
||||
* <p/>
|
||||
* This implementation is optimised that timeout operations are not cancelled on
|
||||
* every operation. Rather timeout are allowed to expire and a check is then made
|
||||
* to see when the last operation took place. If the idle timeout has not expired,
|
||||
* the timeout is rescheduled for the earliest possible time a timeout could occur.
|
||||
*
|
||||
*/
|
||||
public abstract class IdleTimeout
|
||||
{
|
||||
|
@ -43,15 +40,15 @@ public abstract class IdleTimeout
|
|||
private final Scheduler _scheduler;
|
||||
private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
|
||||
private volatile long _idleTimeout;
|
||||
private volatile long _idleTimestamp=System.currentTimeMillis();
|
||||
private volatile long _idleTimestamp = System.currentTimeMillis();
|
||||
|
||||
private final Runnable _idleTask = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
long idleLeft=checkIdleTimeout();
|
||||
if (idleLeft>=0)
|
||||
long idleLeft = checkIdleTimeout();
|
||||
if (idleLeft >= 0)
|
||||
scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout());
|
||||
}
|
||||
};
|
||||
|
@ -61,7 +58,7 @@ public abstract class IdleTimeout
|
|||
*/
|
||||
public IdleTimeout(Scheduler scheduler)
|
||||
{
|
||||
_scheduler=scheduler;
|
||||
_scheduler = scheduler;
|
||||
}
|
||||
|
||||
public long getIdleTimestamp()
|
||||
|
@ -76,14 +73,14 @@ public abstract class IdleTimeout
|
|||
|
||||
public void setIdleTimeout(long idleTimeout)
|
||||
{
|
||||
long old=_idleTimeout;
|
||||
long old = _idleTimeout;
|
||||
_idleTimeout = idleTimeout;
|
||||
|
||||
// Do we have an old timeout
|
||||
if (old>0)
|
||||
if (old > 0)
|
||||
{
|
||||
// if the old was less than or equal to the new timeout, then nothing more to do
|
||||
if (old<=idleTimeout)
|
||||
if (old <= idleTimeout)
|
||||
return;
|
||||
|
||||
// old timeout is too long, so cancel it.
|
||||
|
@ -93,22 +90,22 @@ public abstract class IdleTimeout
|
|||
}
|
||||
|
||||
// If we have a new timeout, then check and reschedule
|
||||
if (idleTimeout>0 && isOpen())
|
||||
if (idleTimeout > 0 && isOpen())
|
||||
_idleTask.run();
|
||||
|
||||
}
|
||||
|
||||
/** This method should be called when non-idle activity has taken place.
|
||||
/**
|
||||
* This method should be called when non-idle activity has taken place.
|
||||
*/
|
||||
public void notIdle()
|
||||
{
|
||||
_idleTimestamp=System.currentTimeMillis();
|
||||
_idleTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void scheduleIdleTimeout(long delay)
|
||||
{
|
||||
Scheduler.Task newTimeout = null;
|
||||
if (isOpen() && delay > 0 && _scheduler!=null)
|
||||
if (isOpen() && delay > 0 && _scheduler != null)
|
||||
newTimeout = _scheduler.schedule(_idleTask, delay, TimeUnit.MILLISECONDS);
|
||||
Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout);
|
||||
if (oldTimeout != null)
|
||||
|
@ -117,10 +114,10 @@ public abstract class IdleTimeout
|
|||
|
||||
public void onOpen()
|
||||
{
|
||||
if (_idleTimeout>0)
|
||||
if (_idleTimeout > 0)
|
||||
_idleTask.run();
|
||||
}
|
||||
|
||||
|
||||
public void onClose()
|
||||
{
|
||||
Scheduler.Task oldTimeout = _timeout.getAndSet(null);
|
||||
|
@ -162,22 +159,23 @@ public abstract class IdleTimeout
|
|||
}
|
||||
}
|
||||
|
||||
return idleLeft>=0?idleLeft:0;
|
||||
return idleLeft >= 0 ? idleLeft : 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** This abstract method is called when the idle timeout has expired.
|
||||
/**
|
||||
* This abstract method is called when the idle timeout has expired.
|
||||
*
|
||||
* @param timeout a TimeoutException
|
||||
*/
|
||||
abstract protected void onIdleExpired(TimeoutException timeout);
|
||||
protected abstract void onIdleExpired(TimeoutException timeout);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** This abstract method should be called to check if idle timeouts
|
||||
/**
|
||||
* This abstract method should be called to check if idle timeouts
|
||||
* should still be checked.
|
||||
*
|
||||
* @return True if the entity monitored should still be checked for idle timeouts
|
||||
*/
|
||||
abstract protected boolean isOpen();
|
||||
public abstract boolean isOpen();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.util.ConcurrentArrayQueue;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
|
@ -93,7 +94,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
|
||||
/**
|
||||
* Get the connect timeout
|
||||
*
|
||||
*
|
||||
* @return the connect timeout (in milliseconds)
|
||||
*/
|
||||
public long getConnectTimeout()
|
||||
|
@ -103,7 +104,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
|
||||
/**
|
||||
* Set the connect timeout (in milliseconds)
|
||||
*
|
||||
*
|
||||
* @param milliseconds the number of milliseconds for the timeout
|
||||
*/
|
||||
public void setConnectTimeout(long milliseconds)
|
||||
|
@ -317,7 +318,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
*/
|
||||
public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
|
||||
{
|
||||
private final Queue<Runnable> _changes = new ConcurrentLinkedQueue<>();
|
||||
private final Queue<Runnable> _changes = new ConcurrentArrayQueue<>();
|
||||
|
||||
private final int _id;
|
||||
private Selector _selector;
|
||||
private volatile Thread _thread;
|
||||
|
@ -364,7 +366,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
if (_runningChanges)
|
||||
_changes.offer(change);
|
||||
else
|
||||
{
|
||||
{
|
||||
// Otherwise we run the queued changes
|
||||
runChanges();
|
||||
// and then directly run the passed change
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
|
@ -67,8 +66,8 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* encrypted endpoint, but if insufficient bytes are read it will NOT call {@link EndPoint#fillInterested(Callback)}.
|
||||
* <p>
|
||||
* It is only the active methods : {@link DecryptedEndPoint#fillInterested(Callback)} and
|
||||
* {@link DecryptedEndPoint#write(Object, Callback, ByteBuffer...)} that may schedule callbacks by calling the encrypted
|
||||
* {@link EndPoint#fillInterested(Callback)} and {@link EndPoint#write(Object, Callback, ByteBuffer...)}
|
||||
* {@link DecryptedEndPoint#write(Callback, ByteBuffer...)} that may schedule callbacks by calling the encrypted
|
||||
* {@link EndPoint#fillInterested(Callback)} and {@link EndPoint#write(Callback, ByteBuffer...)}
|
||||
* methods. For normal data handling, the decrypted fillInterest method will result in an encrypted fillInterest and a decrypted
|
||||
* write will result in an encrypted write. However, due to SSL handshaking requirements, it is also possible for a decrypted fill
|
||||
* to call the encrypted write and for the decrypted flush to call the encrypted fillInterested methods.
|
||||
|
@ -114,7 +113,7 @@ public class SslConnection extends AbstractConnection
|
|||
{
|
||||
try
|
||||
{
|
||||
((SocketBased) endPoint).getSocket().setSoLinger(true,30000);
|
||||
((SocketBased)endPoint).getSocket().setSoLinger(true, 30000);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
|
@ -162,25 +161,12 @@ public class SslConnection extends AbstractConnection
|
|||
super.onClose();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public int getMessagesIn()
|
||||
// {
|
||||
// return _decryptedEndPoint.getConnection().getMessagesIn();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getMessagesOut()
|
||||
// {
|
||||
// return _decryptedEndPoint.getConnection().getMessagesOut();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
getDecryptedEndPoint().getConnection().close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
|
@ -211,7 +197,6 @@ public class SslConnection extends AbstractConnection
|
|||
LOG.debug("onFillable exit {}", getEndPoint());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void onFillInterestedFailed(Throwable cause)
|
||||
{
|
||||
|
@ -220,19 +205,21 @@ public class SslConnection extends AbstractConnection
|
|||
// the decrypted readInterest and/or writeFlusher so that they will attempt
|
||||
// to do the fill and/or flush again and these calls will do the actually
|
||||
// handle the cause.
|
||||
_decryptedEndPoint.getFillInterest().onFail(cause);
|
||||
|
||||
boolean failFlusher = false;
|
||||
synchronized(_decryptedEndPoint)
|
||||
{
|
||||
_decryptedEndPoint.getFillInterest().onFail(cause);
|
||||
|
||||
if (_decryptedEndPoint._flushRequiresFillToProgress)
|
||||
{
|
||||
_decryptedEndPoint._flushRequiresFillToProgress = false;
|
||||
_decryptedEndPoint.getWriteFlusher().onFail(cause);
|
||||
failFlusher = true;
|
||||
}
|
||||
}
|
||||
if (failFlusher)
|
||||
_decryptedEndPoint.getWriteFlusher().onFail(cause);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -250,7 +237,6 @@ public class SslConnection extends AbstractConnection
|
|||
_decryptedEndPoint.getConnection());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public class DecryptedEndPoint extends AbstractEndPoint
|
||||
{
|
||||
private boolean _fillRequiresFlushToProgress;
|
||||
|
@ -266,6 +252,7 @@ public class SslConnection extends AbstractConnection
|
|||
// This means that a write of encrypted data has completed. Writes are done
|
||||
// only if there is a pending writeflusher or a read needed to write
|
||||
// data. In either case the appropriate callback is passed on.
|
||||
boolean fillable = false;
|
||||
synchronized (DecryptedEndPoint.this)
|
||||
{
|
||||
if (DEBUG)
|
||||
|
@ -278,11 +265,12 @@ public class SslConnection extends AbstractConnection
|
|||
if (_fillRequiresFlushToProgress)
|
||||
{
|
||||
_fillRequiresFlushToProgress = false;
|
||||
getFillInterest().fillable();
|
||||
fillable = true;
|
||||
}
|
||||
|
||||
getExecutor().execute(_runCompletWrite);
|
||||
}
|
||||
if (fillable)
|
||||
getFillInterest().fillable();
|
||||
getExecutor().execute(_runCompletWrite);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -291,12 +279,12 @@ public class SslConnection extends AbstractConnection
|
|||
// This means that a write of data has failed. Writes are done
|
||||
// only if there is an active writeflusher or a read needed to write
|
||||
// data. In either case the appropriate callback is passed on.
|
||||
boolean failFiller = false;
|
||||
synchronized (DecryptedEndPoint.this)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} write.failed", SslConnection.this, x);
|
||||
if (_encryptedOutput != null)
|
||||
BufferUtil.clear(_encryptedOutput);
|
||||
BufferUtil.clear(_encryptedOutput);
|
||||
releaseEncryptedOutputBuffer();
|
||||
|
||||
_cannotAcceptMoreAppDataToFlush = false;
|
||||
|
@ -304,11 +292,12 @@ public class SslConnection extends AbstractConnection
|
|||
if (_fillRequiresFlushToProgress)
|
||||
{
|
||||
_fillRequiresFlushToProgress = false;
|
||||
getFillInterest().onFail(x);
|
||||
failFiller = true;
|
||||
}
|
||||
|
||||
getWriteFlusher().onFail(x);
|
||||
}
|
||||
if (failFiller)
|
||||
getFillInterest().onFail(x);
|
||||
getWriteFlusher().onFail(x);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -344,6 +333,7 @@ public class SslConnection extends AbstractConnection
|
|||
// all data could be wrapped. So either we need to write some encrypted data,
|
||||
// OR if we are handshaking we need to read some encrypted data OR
|
||||
// if neither then we should just try the flush again.
|
||||
boolean flush = false;
|
||||
synchronized (DecryptedEndPoint.this)
|
||||
{
|
||||
if (DEBUG)
|
||||
|
@ -355,19 +345,30 @@ public class SslConnection extends AbstractConnection
|
|||
_cannotAcceptMoreAppDataToFlush = true;
|
||||
getEndPoint().write(_writeCallback, _encryptedOutput);
|
||||
}
|
||||
// If we are handshaking and need to read,
|
||||
else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
|
||||
{
|
||||
// we are actually read blocked in order to write
|
||||
_flushRequiresFillToProgress=true;
|
||||
// check if we are actually read blocked in order to write
|
||||
_flushRequiresFillToProgress = true;
|
||||
SslConnection.this.fillInterested();
|
||||
}
|
||||
else if (isOutputShutdown())
|
||||
{
|
||||
getWriteFlusher().onClose();
|
||||
}
|
||||
else
|
||||
{
|
||||
// try the flush again
|
||||
flush = true;
|
||||
}
|
||||
}
|
||||
if (flush)
|
||||
{
|
||||
// If the output is closed,
|
||||
if (isOutputShutdown())
|
||||
{
|
||||
// don't bother writing, just notify of close
|
||||
getWriteFlusher().onClose();
|
||||
}
|
||||
// Else,
|
||||
else
|
||||
{
|
||||
// try to flush what is pending
|
||||
getWriteFlusher().completeWrite();
|
||||
}
|
||||
}
|
||||
|
@ -413,9 +414,11 @@ public class SslConnection extends AbstractConnection
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal readable callback
|
||||
// Get called back on onfillable when then is more data to fill
|
||||
SslConnection.this.fillInterested();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.eclipse.jetty.io;
|
|||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -32,10 +31,10 @@ public class IdleTimeoutTest
|
|||
{
|
||||
volatile boolean _open;
|
||||
volatile TimeoutException _expired;
|
||||
|
||||
|
||||
TimerScheduler _timer;
|
||||
IdleTimeout _timeout;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
|
@ -50,9 +49,9 @@ public class IdleTimeoutTest
|
|||
{
|
||||
_expired=timeout;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isOpen()
|
||||
public boolean isOpen()
|
||||
{
|
||||
return _open;
|
||||
}
|
||||
|
@ -65,7 +64,7 @@ public class IdleTimeoutTest
|
|||
{
|
||||
_open=false;
|
||||
_timer.stop();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,10 +75,10 @@ public class IdleTimeoutTest
|
|||
Thread.sleep(100);
|
||||
_timeout.notIdle();
|
||||
}
|
||||
|
||||
|
||||
Assert.assertNull(_expired);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIdle() throws Exception
|
||||
{
|
||||
|
@ -91,7 +90,7 @@ public class IdleTimeoutTest
|
|||
Thread.sleep(1500);
|
||||
Assert.assertNotNull(_expired);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testClose() throws Exception
|
||||
{
|
||||
|
@ -104,7 +103,7 @@ public class IdleTimeoutTest
|
|||
Thread.sleep(1500);
|
||||
Assert.assertNull(_expired);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testClosed() throws Exception
|
||||
{
|
||||
|
@ -153,7 +152,7 @@ public class IdleTimeoutTest
|
|||
Thread.sleep(1000);
|
||||
Assert.assertNotNull(_expired);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
|
@ -8,24 +9,40 @@
|
|||
<artifactId>jetty-osgi-npn</artifactId>
|
||||
<name>Jetty :: OSGi NPN Fragment</name>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<bundle-symbolic-name>org.eclipse.jetty.osgi.npn.fragment</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Bundle-ManifestVersion>2</Bundle-ManifestVersion>
|
||||
<Bundle-SymbolicName>org.eclipse.jetty.osgi.npn.fragment;singleton:=true</Bundle-SymbolicName>
|
||||
<Bundle-Name>Jetty OSGi NPN Fragment</Bundle-Name>
|
||||
<Bundle-Version>9.0.0</Bundle-Version>
|
||||
<Export-Package>org.eclipse.jetty.npn;version="1.1.2"</Export-Package>
|
||||
<Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>parse-version</id>
|
||||
<goals>
|
||||
<goal>parse-version</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Bundle-ManifestVersion>2</Bundle-ManifestVersion>
|
||||
<Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
|
||||
<Bundle-Name>Jetty OSGi NPN Fragment</Bundle-Name>
|
||||
<Bundle-Version>${parsedVersion.osgiVersion}</Bundle-Version>
|
||||
<Export-Package>org.eclipse.jetty.npn;version="1.1.5"</Export-Package>
|
||||
<Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<felixversion>4.0.3</felixversion>
|
||||
<injection.bundle.version>1.0</injection.bundle.version>
|
||||
<runner.version>1.7.6</runner.version>
|
||||
<npn-version>1.1.2.v20130305</npn-version>
|
||||
<npn-version>1.1.5.v20130313</npn-version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Pax Exam Dependencies -->
|
||||
|
|
|
@ -17,6 +17,14 @@
|
|||
<!-- Consult the javadoc of o.e.j.util.ssl.SslContextFactory -->
|
||||
<!-- o.e.j.server.HttpConnectionFactory for all configuration -->
|
||||
<!-- that may be set here. -->
|
||||
<!-- -->
|
||||
<!-- The keyManagerPassword is passed as the password arg to -->
|
||||
<!-- KeyManagerFactory.init(...) -->
|
||||
<!-- If there is no keymanagerpassword, then the -->
|
||||
<!-- keystorepassword is used instead. -->
|
||||
<!-- If there is no trustmanager set, then the keystore is used -->
|
||||
<!-- as the trust store and the keystorepassword is used as the -->
|
||||
<!-- truststore password. -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
|
||||
<Set name="KeyStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||
|
@ -24,6 +32,7 @@
|
|||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="EndpointIdentificationAlgorithm"></Set>
|
||||
<Set name="ExcludeCipherSuites">
|
||||
<Array type="String">
|
||||
<Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<Arg>
|
||||
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
|
||||
<Set name="requestLog">
|
||||
<New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
|
||||
<New id="RequestLogImpl" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
|
||||
<Set name="filename"><Property name="jetty.logs" default="./logs" />/yyyy_mm_dd.request.log</Set>
|
||||
<Set name="filenameDateFormat">yyyy_MM_dd</Set>
|
||||
<Set name="retainDays">90</Set>
|
||||
|
|
|
@ -44,8 +44,14 @@
|
|||
<!-- =========================================================== -->
|
||||
<Arg name="threadpool">
|
||||
<New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
|
||||
<Set name="minThreads">10</Set>
|
||||
<Set name="maxThreads">200</Set>
|
||||
<Arg name="minThreads">10</Arg>
|
||||
<Arg name="maxThreads">200</Arg>
|
||||
<Arg name="idleTimeout">60000</Arg>
|
||||
<Arg name="queue">
|
||||
<New class="org.eclipse.jetty.util.ConcurrentArrayBlockingQueue$Unbounded">
|
||||
<Arg>32</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
<Set name="detailedDump">false</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.ConcurrentArrayBlockingQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* An asynchronously writing NCSA Request Log
|
||||
*/
|
||||
public class AsyncNCSARequestLog extends NCSARequestLog
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AsyncNCSARequestLog.class);
|
||||
private final BlockingQueue<String> _queue;
|
||||
private transient WriterThread _thread;
|
||||
private boolean _warnedFull;
|
||||
|
||||
public AsyncNCSARequestLog()
|
||||
{
|
||||
this(null,null);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(BlockingQueue<String> queue)
|
||||
{
|
||||
this(null,queue);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(String filename)
|
||||
{
|
||||
this(filename,null);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(String filename,BlockingQueue<String> queue)
|
||||
{
|
||||
super(filename);
|
||||
if (queue==null)
|
||||
queue=new ConcurrentArrayBlockingQueue.Bounded<String>(1024);
|
||||
_queue=queue;
|
||||
}
|
||||
|
||||
private class WriterThread extends Thread
|
||||
{
|
||||
WriterThread()
|
||||
{
|
||||
setName("AsyncNCSARequestLog@"+Integer.toString(AsyncNCSARequestLog.this.hashCode(),16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (isRunning())
|
||||
{
|
||||
try
|
||||
{
|
||||
String log = _queue.poll(10,TimeUnit.SECONDS);
|
||||
if (log!=null)
|
||||
AsyncNCSARequestLog.super.write(log);
|
||||
|
||||
while(!_queue.isEmpty())
|
||||
{
|
||||
log=_queue.poll();
|
||||
if (log!=null)
|
||||
AsyncNCSARequestLog.super.write(log);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
_thread = new WriterThread();
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
_thread.interrupt();
|
||||
_thread.join();
|
||||
super.doStop();
|
||||
_thread=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(String log) throws IOException
|
||||
{
|
||||
if (!_queue.offer(log))
|
||||
{
|
||||
if (_warnedFull)
|
||||
LOG.warn("Log Queue overflow");
|
||||
_warnedFull=true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -55,6 +55,14 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(NCSARequestLog.class);
|
||||
private static ThreadLocal<StringBuilder> _buffers = new ThreadLocal<StringBuilder>()
|
||||
{
|
||||
@Override
|
||||
protected StringBuilder initialValue()
|
||||
{
|
||||
return new StringBuilder(256);
|
||||
}
|
||||
};
|
||||
|
||||
private String _filename;
|
||||
private boolean _extended;
|
||||
|
@ -468,7 +476,8 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
if (_fileOut == null)
|
||||
return;
|
||||
|
||||
StringBuilder buf= new StringBuilder(256);
|
||||
StringBuilder buf= _buffers.get();
|
||||
buf.setLength(0);
|
||||
|
||||
if (_logServer)
|
||||
{
|
||||
|
@ -584,22 +593,29 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
}
|
||||
|
||||
buf.append(StringUtil.__LINE_SEPARATOR);
|
||||
|
||||
String log = buf.toString();
|
||||
synchronized(this)
|
||||
{
|
||||
if (_writer==null)
|
||||
return;
|
||||
_writer.write(log);
|
||||
_writer.flush();
|
||||
}
|
||||
write(log);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void write(String log) throws IOException
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (_writer==null)
|
||||
return;
|
||||
_writer.write(log);
|
||||
_writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Writes extended request and response information to the output stream.
|
||||
|
@ -669,7 +685,10 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
else
|
||||
_ignorePathMap = null;
|
||||
|
||||
_writer = new OutputStreamWriter(_out);
|
||||
synchronized(this)
|
||||
{
|
||||
_writer = new OutputStreamWriter(_out);
|
||||
}
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
|
|
|
@ -280,10 +280,12 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (getStopAtShutdown()) {
|
||||
ShutdownThread.register(this);
|
||||
ShutdownMonitor.getInstance().start(); // initialize
|
||||
if (getStopAtShutdown())
|
||||
{
|
||||
ShutdownThread.register(this);
|
||||
}
|
||||
|
||||
ShutdownMonitor.getInstance().start(); // initialize
|
||||
|
||||
LOG.info("jetty-"+getVersion());
|
||||
HttpGenerator.setServerVersion(getVersion());
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
|
|||
* <p>
|
||||
* Commands "stop" and "status" are currently supported.
|
||||
*/
|
||||
public class ShutdownMonitor extends Thread
|
||||
public class ShutdownMonitor
|
||||
{
|
||||
// Implementation of safe lazy init, using Initialization on Demand Holder technique.
|
||||
static class Holder
|
||||
|
@ -53,11 +53,164 @@ public class ShutdownMonitor extends Thread
|
|||
return Holder.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShutdownMonitorThread
|
||||
*
|
||||
* Thread for listening to STOP.PORT for command to stop Jetty.
|
||||
* If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
|
||||
* called after the stop.
|
||||
*
|
||||
*/
|
||||
public class ShutdownMonitorThread extends Thread
|
||||
{
|
||||
|
||||
public ShutdownMonitorThread ()
|
||||
{
|
||||
setDaemon(true);
|
||||
setName("ShutdownMonitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverSocket != null)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String receivedKey = lin.readLine();
|
||||
if (!key.equals(receivedKey))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
debug("command=%s",cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
close(socket);
|
||||
socket = null;
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
// Reply to client
|
||||
out.write("OK\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if (isAlive())
|
||||
{
|
||||
System.err.printf("ShutdownMonitorThread already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
|
||||
startListenSocket();
|
||||
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
System.err.println("Starting ShutdownMonitorThread");
|
||||
super.start();
|
||||
}
|
||||
|
||||
private void startListenSocket()
|
||||
{
|
||||
if (port < 0)
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (port == 0)
|
||||
{
|
||||
// server assigned port in use
|
||||
port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",port);
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
// create random key
|
||||
key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",key);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + port + ": " + e.toString());
|
||||
serverSocket = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d",port);
|
||||
debug("STOP.KEY=%s",key);
|
||||
debug("%s",serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean DEBUG;
|
||||
private int port;
|
||||
private String key;
|
||||
private boolean exitVm;
|
||||
private ServerSocket serverSocket;
|
||||
private ShutdownMonitorThread thread;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a ShutdownMonitor using configuration from the System properties.
|
||||
|
@ -75,7 +228,7 @@ public class ShutdownMonitor extends Thread
|
|||
|
||||
// Use values passed thru via /jetty-start/
|
||||
this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
|
||||
this.key = props.getProperty("STOP.KEY","eclipse");
|
||||
this.key = props.getProperty("STOP.KEY",null);
|
||||
this.exitVm = true;
|
||||
}
|
||||
|
||||
|
@ -149,77 +302,6 @@ public class ShutdownMonitor extends Thread
|
|||
return exitVm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverSocket != null)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String key = lin.readLine();
|
||||
if (!this.key.equals(key))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
debug("command=%s",cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
close(socket);
|
||||
socket = null;
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
// Reply to client
|
||||
out.write("OK\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebug(boolean flag)
|
||||
{
|
||||
|
@ -228,90 +310,71 @@ public class ShutdownMonitor extends Thread
|
|||
|
||||
public void setExitVm(boolean exitVm)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
|
||||
public void setKey(String key)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.key = key;
|
||||
}
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setPort(int port)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
}
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if (isAlive())
|
||||
{
|
||||
System.err.printf("ShutdownMonitor already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
startListenSocket();
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
super.start();
|
||||
}
|
||||
|
||||
private void startListenSocket()
|
||||
{
|
||||
if (this.port < 0)
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
setDaemon(true);
|
||||
setName("ShutdownMonitor");
|
||||
|
||||
this.serverSocket = new ServerSocket(this.port,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (this.port == 0)
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
// server assigned port in use
|
||||
this.port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",this.port);
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
|
||||
if (this.key == null)
|
||||
{
|
||||
// create random key
|
||||
this.key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",this.key);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + this.port + ": " + e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d",this.port);
|
||||
debug("STOP.KEY=%s",this.key);
|
||||
debug("%s",serverSocket);
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
protected void start() throws Exception
|
||||
{
|
||||
ShutdownMonitorThread t = null;
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
System.err.printf("ShutdownMonitorThread already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
|
||||
thread = new ShutdownMonitorThread();
|
||||
t = thread;
|
||||
}
|
||||
|
||||
if (t != null)
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
||||
protected boolean isAlive ()
|
||||
{
|
||||
boolean result = false;
|
||||
synchronized (this)
|
||||
{
|
||||
result = (thread != null && thread.isAlive());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -95,7 +96,14 @@ public class AsyncStressTest
|
|||
@Stress("High connection count")
|
||||
public void testAsync() throws Throwable
|
||||
{
|
||||
doConnections(1600,240);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doConnections(1600,240);
|
||||
}
|
||||
else
|
||||
{
|
||||
doConnections(80,80);
|
||||
}
|
||||
}
|
||||
|
||||
private void doConnections(int connections,final int loops) throws Throwable
|
||||
|
|
|
@ -49,8 +49,13 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
|
|||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data()
|
||||
{
|
||||
Object[][] data = new Object[][]{{HttpVersion.HTTP_1_0.asString(), true}, {HttpVersion.HTTP_1_0.asString(),
|
||||
false}, {HttpVersion.HTTP_1_1.asString(), true}, {HttpVersion.HTTP_1_1.asString(), false}};
|
||||
Object[][] data = new Object[][]
|
||||
{
|
||||
{HttpVersion.HTTP_1_0.asString(), true},
|
||||
{HttpVersion.HTTP_1_1.asString(), true},
|
||||
{HttpVersion.HTTP_1_0.asString(), false},
|
||||
{HttpVersion.HTTP_1_1.asString(), false}
|
||||
};
|
||||
return Arrays.asList(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -42,7 +43,7 @@ import org.junit.Before;
|
|||
public class HttpServerTestFixture
|
||||
{ // Useful constants
|
||||
protected static final long PAUSE=10L;
|
||||
protected static final int LOOPS=50;
|
||||
protected static final int LOOPS=PropertyFlag.isEnabled("test.stress")?250:50;
|
||||
|
||||
protected Server _server;
|
||||
protected URI _serverURI;
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* ShutdownMonitorTest
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ShutdownMonitorTest
|
||||
{
|
||||
|
||||
|
||||
@Test
|
||||
public void testShutdown ()
|
||||
throws Exception
|
||||
{
|
||||
|
||||
//test port and key assignment
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setExitVm(false);
|
||||
ShutdownMonitor.getInstance().start();
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
|
||||
//try starting a 2nd time (should be ignored)
|
||||
ShutdownMonitor.getInstance().start();
|
||||
|
||||
|
||||
stop(port,key,true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
|
||||
//should be able to change port and key because it is stopped
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setKey("foo");
|
||||
ShutdownMonitor.getInstance().start();
|
||||
|
||||
key = ShutdownMonitor.getInstance().getKey();
|
||||
port = ShutdownMonitor.getInstance().getPort();
|
||||
assertTrue(ShutdownMonitor.getInstance().isAlive());
|
||||
|
||||
stop(port,key,true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
}
|
||||
|
||||
|
||||
public void stop (int port, String key, boolean check)
|
||||
throws Exception
|
||||
{
|
||||
Socket s = null;
|
||||
|
||||
try
|
||||
{
|
||||
//send stop command
|
||||
s = new Socket(InetAddress.getByName("127.0.0.1"),port);
|
||||
|
||||
OutputStream out = s.getOutputStream();
|
||||
out.write((key + "\r\nstop\r\n").getBytes());
|
||||
out.flush();
|
||||
|
||||
if (check)
|
||||
{
|
||||
//wait a little
|
||||
Thread.currentThread().sleep(600);
|
||||
|
||||
//check for stop confirmation
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
|
||||
String response;
|
||||
if ((response = lin.readLine()) != null)
|
||||
{
|
||||
assertEquals("Stopped", response);
|
||||
}
|
||||
else
|
||||
throw new IllegalStateException("No stop confirmation");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (s != null) s.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -130,35 +131,37 @@ public class StressTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testNonPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
|
||||
|
||||
doThreads(10,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(20,20,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doThreads(20,20,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
|
||||
|
||||
doThreads(10,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(40,40,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doThreads(40,40,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
}
|
||||
}
|
||||
|
||||
private void doThreads(int threadCount, final int loops, final boolean persistent) throws Throwable
|
||||
|
|
|
@ -97,7 +97,19 @@ public class FilterHolder extends Holder<Filter>
|
|||
super.stop();
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void initialize() throws Exception
|
||||
{
|
||||
super.initialize();
|
||||
|
||||
if (_filter==null)
|
||||
{
|
||||
try
|
||||
|
@ -123,6 +135,7 @@ public class FilterHolder extends Holder<Filter>
|
|||
_filter.init(_config);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void doStop()
|
||||
|
|
|
@ -82,6 +82,19 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
{
|
||||
return _extInstance;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Do any setup necessary after starting
|
||||
* @throws Exception
|
||||
*/
|
||||
public void initialize()
|
||||
throws Exception
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException("Not started: "+this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -741,6 +741,7 @@ public class ServletHandler extends ScopedHandler
|
|||
try
|
||||
{
|
||||
h.start();
|
||||
h.initialize();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -280,7 +280,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
_enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void doStart()
|
||||
throws Exception
|
||||
|
@ -319,7 +319,17 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
|
||||
if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
|
||||
_servlet = new SingleThreadedWrapper();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void initialize ()
|
||||
throws Exception
|
||||
{
|
||||
super.initialize();
|
||||
|
||||
if (_extInstance || _initOnStartup)
|
||||
{
|
||||
try
|
||||
|
@ -335,7 +345,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void doStop()
|
||||
throws Exception
|
||||
|
|
|
@ -98,7 +98,9 @@ public abstract class AbstractDoSFilterTest
|
|||
public void startFilters() throws Exception
|
||||
{
|
||||
_dosFilter.start();
|
||||
_dosFilter.initialize();
|
||||
_timeoutFilter.start();
|
||||
_timeoutFilter.initialize();
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<name>Jetty :: SPDY :: Parent</name>
|
||||
|
||||
<properties>
|
||||
<npn.version>1.1.2.v20130305</npn.version>
|
||||
<npn.version>1.1.5.v20130313</npn.version>
|
||||
<npn.api.version>1.1.0.v20120525</npn.api.version>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -963,6 +963,7 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
{
|
||||
FrameBytes frameBytes = null;
|
||||
ByteBuffer buffer = null;
|
||||
boolean failFrameBytes = false;
|
||||
synchronized (queue)
|
||||
{
|
||||
if (flushing || queue.isEmpty())
|
||||
|
@ -982,11 +983,7 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
{
|
||||
queue.remove(i);
|
||||
if (stream != null && stream.isReset() && !(frameBytes instanceof ControlFrameBytes))
|
||||
{
|
||||
frameBytes.fail(new StreamException(stream.getId(), StreamStatus.INVALID_STREAM,
|
||||
"Stream: " + stream + " is reset!"));
|
||||
return;
|
||||
}
|
||||
failFrameBytes = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1001,10 +998,21 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
if (buffer == null)
|
||||
return;
|
||||
|
||||
flushing = true;
|
||||
LOG.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size());
|
||||
if (!failFrameBytes)
|
||||
{
|
||||
flushing = true;
|
||||
LOG.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size());
|
||||
}
|
||||
}
|
||||
if (failFrameBytes)
|
||||
{
|
||||
frameBytes.fail(new StreamException(frameBytes.getStream().getId(), StreamStatus.INVALID_STREAM,
|
||||
"Stream: " + frameBytes.getStream() + " is reset!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
write(buffer, frameBytes);
|
||||
}
|
||||
write(buffer, frameBytes);
|
||||
}
|
||||
|
||||
private void append(FrameBytes frameBytes)
|
||||
|
|
|
@ -96,6 +96,12 @@
|
|||
<artifactId>jetty-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.npn</groupId>
|
||||
<artifactId>npn-api</artifactId>
|
||||
|
|
|
@ -24,6 +24,18 @@
|
|||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="EndpointIdentificationAlgorithm"></Set>
|
||||
<Set name="ExcludeCipherSuites">
|
||||
<Array type="String">
|
||||
<Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</New>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.server.HttpConfiguration;
|
|||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.spdy.StreamException;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.HeadersInfo;
|
||||
import org.eclipse.jetty.spdy.api.PushInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
@ -198,6 +199,8 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
{
|
||||
if (!stream.isUnidirectional())
|
||||
stream.reply(replyInfo, new Callback.Adapter());
|
||||
else
|
||||
stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), new Callback.Adapter());
|
||||
|
||||
Fields responseHeaders = replyInfo.getHeaders();
|
||||
short version = stream.getSession().getVersion();
|
||||
|
@ -230,19 +233,17 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
|
||||
private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
|
||||
{
|
||||
final Fields requestHeaders = new Fields();
|
||||
final Fields newRequestHeaders = new Fields(requestHeaders, false);
|
||||
short version = stream.getSession().getVersion();
|
||||
requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
requestHeaders.put(scheme);
|
||||
requestHeaders.put(host);
|
||||
requestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
|
||||
newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
newRequestHeaders.put(scheme);
|
||||
newRequestHeaders.put(host);
|
||||
newRequestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
|
||||
String referrer = scheme.value() + "://" + host.value() + uri.value();
|
||||
requestHeaders.put("referer", referrer);
|
||||
// Remember support for gzip encoding
|
||||
requestHeaders.put(requestHeaders.get("accept-encoding"));
|
||||
requestHeaders.put("x-spdy-push", "true");
|
||||
return requestHeaders;
|
||||
newRequestHeaders.put("referer", referrer);
|
||||
newRequestHeaders.put("x-spdy-push", "true");
|
||||
return newRequestHeaders;
|
||||
}
|
||||
|
||||
private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
|
||||
|
@ -257,8 +258,6 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
pushHeaders.put(scheme);
|
||||
pushHeaders.put(host);
|
||||
}
|
||||
pushHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200");
|
||||
pushHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
return pushHeaders;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.eclipse.jetty.server.ConnectionFactory;
|
|||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipHandler;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.HeadersInfo;
|
||||
import org.eclipse.jetty.spdy.api.PushInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.RstInfo;
|
||||
|
@ -155,7 +157,8 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
|
||||
private InetSocketAddress createServer() throws Exception
|
||||
{
|
||||
return startHTTPServer(version, new AbstractHandler()
|
||||
GzipHandler gzipHandler = new GzipHandler();
|
||||
gzipHandler.setHandler(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
|
@ -171,6 +174,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
return startHTTPServer(version, gzipHandler);
|
||||
}
|
||||
|
||||
private Session sendMainRequestAndCSSRequest() throws Exception
|
||||
|
@ -240,6 +244,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
|
||||
final CountDownLatch pushResponseHeaders = new CountDownLatch(1);
|
||||
Session session2 = startClient(version, serverAddress, null);
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -257,10 +262,19 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
is(true));
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersInfo headersInfo)
|
||||
{
|
||||
Fields headers = headersInfo.getHeaders();
|
||||
if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version), "200 OK")
|
||||
&& validateHeader(headers, HTTPSPDYHeader.VERSION.name(version),
|
||||
"HTTP/1.1") && validateHeader(headers, "content-encoding", "gzip"))
|
||||
pushResponseHeaders.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
|
@ -797,9 +811,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
|
||||
private void validateHeaders(Fields headers, CountDownLatch pushSynHeadersValid)
|
||||
{
|
||||
if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version), "200")
|
||||
&& validateHeader(headers, HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1")
|
||||
&& validateUriHeader(headers))
|
||||
if (validateUriHeader(headers))
|
||||
pushSynHeadersValid.countDown();
|
||||
}
|
||||
|
||||
|
@ -808,7 +820,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
Fields.Field header = headers.get(name);
|
||||
if (header != null && expectedValue.equals(header.value()))
|
||||
return true;
|
||||
System.out.println(name + " not valid! " + headers);
|
||||
System.out.println(name + " not valid! Expected: " + expectedValue + " headers received:" + headers);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -842,6 +854,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
Fields requestHeaders = new Fields();
|
||||
requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) " +
|
||||
"Gecko/20100101 Firefox/16.0");
|
||||
requestHeaders.put("accept-encoding", "gzip");
|
||||
requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
requestHeaders.put(HTTPSPDYHeader.URI.name(version), resource);
|
||||
requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
|
|
|
@ -62,6 +62,9 @@ import org.junit.runner.Description;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class ProxySPDYToSPDYTest
|
||||
{
|
||||
|
@ -186,6 +189,40 @@ public class ProxySPDYToSPDYTest
|
|||
|
||||
client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
|
||||
}
|
||||
@Test
|
||||
public void testSYNThenRSTFromUpstreamServer() throws Exception
|
||||
{
|
||||
final String header = "foo";
|
||||
InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Fields requestHeaders = synInfo.getHeaders();
|
||||
Assert.assertNotNull(requestHeaders.get("via"));
|
||||
Assert.assertNotNull(requestHeaders.get(header));
|
||||
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
|
||||
|
||||
final CountDownLatch resetLatch = new CountDownLatch(1);
|
||||
Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onRst(Session session, RstInfo rstInfo)
|
||||
{
|
||||
resetLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
headers.put(header, "bar");
|
||||
client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter());
|
||||
|
||||
assertThat("reset is received by client", resetLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSYNThenREPLYAndDATA() throws Exception
|
||||
|
|
|
@ -53,12 +53,12 @@ public class BlockingArrayQueue<E> extends AbstractList<E> implements BlockingQu
|
|||
* by 15 slots to avoid false sharing with the array length
|
||||
* (stored before the first element of the array itself).
|
||||
*/
|
||||
private static final int HEAD_OFFSET = 15;
|
||||
private static final int HEAD_OFFSET = MemoryUtils.getIntegersPerCacheLine() - 1;
|
||||
/**
|
||||
* The tail offset in the {@link #_indexes} array, displaced
|
||||
* by 16 slots from the head to avoid false sharing with it.
|
||||
*/
|
||||
private static final int TAIL_OFFSET = 31;
|
||||
private static final int TAIL_OFFSET = HEAD_OFFSET + MemoryUtils.getIntegersPerCacheLine();
|
||||
/**
|
||||
* Default initial capacity, 128.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Common functionality for a blocking version of {@link ConcurrentArrayQueue}.
|
||||
*
|
||||
* @see Unbounded
|
||||
* @see Bounded
|
||||
* @param <E>
|
||||
*/
|
||||
public abstract class ConcurrentArrayBlockingQueue<E> extends ConcurrentArrayQueue<E> implements BlockingQueue<E>
|
||||
{
|
||||
private final Lock _lock = new ReentrantLock();
|
||||
private final Condition _consumer = _lock.newCondition();
|
||||
|
||||
public ConcurrentArrayBlockingQueue(int blockSize)
|
||||
{
|
||||
super(blockSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll()
|
||||
{
|
||||
E result = super.poll();
|
||||
if (result != null && decrementAndGetSize() > 0)
|
||||
signalConsumer();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
boolean result = super.remove(o);
|
||||
if (result && decrementAndGetSize() > 0)
|
||||
signalConsumer();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract int decrementAndGetSize();
|
||||
|
||||
protected void signalConsumer()
|
||||
{
|
||||
final Lock lock = _lock;
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
_consumer.signal();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E take() throws InterruptedException
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
E result = poll();
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
final Lock lock = _lock;
|
||||
lock.lockInterruptibly();
|
||||
try
|
||||
{
|
||||
if (size() == 0)
|
||||
{
|
||||
_consumer.await();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll(long timeout, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
long nanos = unit.toNanos(timeout);
|
||||
|
||||
while (true)
|
||||
{
|
||||
// TODO should reduce nanos if we spin here
|
||||
|
||||
E result = poll();
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
final Lock lock = _lock;
|
||||
lock.lockInterruptibly();
|
||||
try
|
||||
{
|
||||
if (size() == 0)
|
||||
{
|
||||
if (nanos <= 0)
|
||||
return null;
|
||||
nanos = _consumer.awaitNanos(nanos);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> c)
|
||||
{
|
||||
return drainTo(c, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> c, int maxElements)
|
||||
{
|
||||
if (c == this)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
int added = 0;
|
||||
while (added < maxElements)
|
||||
{
|
||||
E element = poll();
|
||||
if (element == null)
|
||||
break;
|
||||
c.add(element);
|
||||
++added;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* An unbounded, blocking version of {@link ConcurrentArrayQueue}.
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public static class Unbounded<E> extends ConcurrentArrayBlockingQueue<E>
|
||||
{
|
||||
private static final int SIZE_LEFT_OFFSET = MemoryUtils.getLongsPerCacheLine() - 1;
|
||||
private static final int SIZE_RIGHT_OFFSET = SIZE_LEFT_OFFSET + MemoryUtils.getLongsPerCacheLine();
|
||||
|
||||
private final AtomicLongArray _sizes = new AtomicLongArray(SIZE_RIGHT_OFFSET+1);
|
||||
|
||||
public Unbounded()
|
||||
{
|
||||
this(DEFAULT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
public Unbounded(int blockSize)
|
||||
{
|
||||
super(blockSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E item)
|
||||
{
|
||||
boolean result = super.offer(item);
|
||||
if (result && getAndIncrementSize() == 0)
|
||||
signalConsumer();
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getAndIncrementSize()
|
||||
{
|
||||
long sizeRight = _sizes.getAndIncrement(SIZE_RIGHT_OFFSET);
|
||||
long sizeLeft = _sizes.get(SIZE_LEFT_OFFSET);
|
||||
return (int)(sizeRight - sizeLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int decrementAndGetSize()
|
||||
{
|
||||
long sizeLeft = _sizes.incrementAndGet(SIZE_LEFT_OFFSET);
|
||||
long sizeRight = _sizes.get(SIZE_RIGHT_OFFSET);
|
||||
return (int)(sizeRight - sizeLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
long sizeLeft = _sizes.get(SIZE_LEFT_OFFSET);
|
||||
long sizeRight = _sizes.get(SIZE_RIGHT_OFFSET);
|
||||
return (int)(sizeRight - sizeLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remainingCapacity()
|
||||
{
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(E element) throws InterruptedException
|
||||
{
|
||||
offer(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E element, long timeout, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
return offer(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A bounded, blocking version of {@link ConcurrentArrayQueue}.
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public static class Bounded<E> extends ConcurrentArrayBlockingQueue<E>
|
||||
{
|
||||
private final AtomicInteger _size = new AtomicInteger();
|
||||
private final Lock _lock = new ReentrantLock();
|
||||
private final Condition _producer = _lock.newCondition();
|
||||
private final int _capacity;
|
||||
|
||||
public Bounded(int capacity)
|
||||
{
|
||||
this(DEFAULT_BLOCK_SIZE, capacity);
|
||||
}
|
||||
|
||||
public Bounded(int blockSize, int capacity)
|
||||
{
|
||||
super(blockSize);
|
||||
this._capacity = capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E item)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int size = size();
|
||||
int nextSize = size + 1;
|
||||
|
||||
if (nextSize > _capacity)
|
||||
return false;
|
||||
|
||||
if (_size.compareAndSet(size, nextSize))
|
||||
{
|
||||
if (super.offer(item))
|
||||
{
|
||||
if (size == 0)
|
||||
signalConsumer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
decrementAndGetSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll()
|
||||
{
|
||||
E result = super.poll();
|
||||
if (result != null)
|
||||
signalProducer();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
boolean result = super.remove(o);
|
||||
if (result)
|
||||
signalProducer();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int decrementAndGetSize()
|
||||
{
|
||||
return _size.decrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return _size.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remainingCapacity()
|
||||
{
|
||||
return _capacity - size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(E item) throws InterruptedException
|
||||
{
|
||||
item = Objects.requireNonNull(item);
|
||||
|
||||
while (true)
|
||||
{
|
||||
final Lock lock = _lock;
|
||||
lock.lockInterruptibly();
|
||||
try
|
||||
{
|
||||
if (size() == _capacity)
|
||||
_producer.await();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
if (offer(item))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E item, long timeout, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
item = Objects.requireNonNull(item);
|
||||
|
||||
long nanos = unit.toNanos(timeout);
|
||||
while (true)
|
||||
{
|
||||
final Lock lock = _lock;
|
||||
lock.lockInterruptibly();
|
||||
try
|
||||
{
|
||||
if (size() == _capacity)
|
||||
{
|
||||
if (nanos <= 0)
|
||||
return false;
|
||||
nanos = _producer.awaitNanos(nanos);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
if (offer(item))
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> c, int maxElements)
|
||||
{
|
||||
int result = super.drainTo(c, maxElements);
|
||||
if (result > 0)
|
||||
signalProducers();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
super.clear();
|
||||
signalProducers();
|
||||
}
|
||||
|
||||
private void signalProducer()
|
||||
{
|
||||
final Lock lock = _lock;
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
_producer.signal();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void signalProducers()
|
||||
{
|
||||
final Lock lock = _lock;
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
_producer.signalAll();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,570 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
/**
|
||||
* A concurrent, unbounded implementation of {@link Queue} that uses singly-linked array blocks
|
||||
* to store elements.
|
||||
* <p/>
|
||||
* This class is a drop-in replacement for {@link ConcurrentLinkedQueue}, with similar performance
|
||||
* but producing less garbage because arrays are used to store elements rather than nodes.
|
||||
* <p/>
|
||||
* The algorithm used is a variation of the algorithm from Gidenstam, Sundell and Tsigas
|
||||
* (http://www.adm.hb.se/~AGD/Presentations/CacheAwareQueue_OPODIS.pdf).
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class ConcurrentArrayQueue<T> extends AbstractQueue<T>
|
||||
{
|
||||
public static final int DEFAULT_BLOCK_SIZE = 512;
|
||||
public static final Object REMOVED_ELEMENT = new Object()
|
||||
{
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "X";
|
||||
}
|
||||
};
|
||||
|
||||
private static final int HEAD_OFFSET = MemoryUtils.getIntegersPerCacheLine() - 1;
|
||||
private static final int TAIL_OFFSET = MemoryUtils.getIntegersPerCacheLine()*2 -1;
|
||||
|
||||
private final AtomicReferenceArray<Block<T>> _blocks = new AtomicReferenceArray<>(TAIL_OFFSET + 1);
|
||||
private final int _blockSize;
|
||||
|
||||
public ConcurrentArrayQueue()
|
||||
{
|
||||
this(DEFAULT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
public ConcurrentArrayQueue(int blockSize)
|
||||
{
|
||||
_blockSize = blockSize;
|
||||
Block<T> block = newBlock();
|
||||
_blocks.set(HEAD_OFFSET,block);
|
||||
_blocks.set(TAIL_OFFSET,block);
|
||||
}
|
||||
|
||||
public int getBlockSize()
|
||||
{
|
||||
return _blockSize;
|
||||
}
|
||||
|
||||
protected Block<T> getHeadBlock()
|
||||
{
|
||||
return _blocks.get(HEAD_OFFSET);
|
||||
}
|
||||
|
||||
protected Block<T> getTailBlock()
|
||||
{
|
||||
return _blocks.get(TAIL_OFFSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(T item)
|
||||
{
|
||||
item = Objects.requireNonNull(item);
|
||||
|
||||
final Block<T> initialTailBlock = getTailBlock();
|
||||
Block<T> currentTailBlock = initialTailBlock;
|
||||
int tail = currentTailBlock.tail();
|
||||
while (true)
|
||||
{
|
||||
if (tail == getBlockSize())
|
||||
{
|
||||
Block<T> nextTailBlock = currentTailBlock.next();
|
||||
if (nextTailBlock == null)
|
||||
{
|
||||
nextTailBlock = newBlock();
|
||||
if (currentTailBlock.link(nextTailBlock))
|
||||
{
|
||||
// Linking succeeded, loop
|
||||
currentTailBlock = nextTailBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Concurrent linking, use other block and loop
|
||||
currentTailBlock = currentTailBlock.next();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not at last block, loop
|
||||
currentTailBlock = nextTailBlock;
|
||||
}
|
||||
tail = currentTailBlock.tail();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentTailBlock.peek(tail) == null)
|
||||
{
|
||||
if (currentTailBlock.store(tail, item))
|
||||
{
|
||||
// Item stored
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Concurrent store, try next index
|
||||
++tail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not free, try next index
|
||||
++tail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateTailBlock(initialTailBlock, currentTailBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateTailBlock(Block<T> oldTailBlock, Block<T> newTailBlock)
|
||||
{
|
||||
// Update the tail block pointer if needs to
|
||||
if (oldTailBlock != newTailBlock)
|
||||
{
|
||||
// The tail block pointer is allowed to lag behind.
|
||||
// If this update fails, it means that other threads
|
||||
// have filled this block and installed a new one.
|
||||
casTailBlock(oldTailBlock, newTailBlock);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean casTailBlock(Block<T> current, Block<T> update)
|
||||
{
|
||||
return _blocks.compareAndSet(TAIL_OFFSET,current,update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T poll()
|
||||
{
|
||||
final Block<T> initialHeadBlock = getHeadBlock();
|
||||
Block<T> currentHeadBlock = initialHeadBlock;
|
||||
int head = currentHeadBlock.head();
|
||||
T result = null;
|
||||
while (true)
|
||||
{
|
||||
if (head == getBlockSize())
|
||||
{
|
||||
Block<T> nextHeadBlock = currentHeadBlock.next();
|
||||
if (nextHeadBlock == null)
|
||||
{
|
||||
// We could have read that the next head block was null
|
||||
// but another thread allocated a new block and stored a
|
||||
// new item. This thread could not detect this, but that
|
||||
// is ok, otherwise we would not be able to exit this loop.
|
||||
|
||||
// Queue is empty
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use next block and loop
|
||||
currentHeadBlock = nextHeadBlock;
|
||||
head = currentHeadBlock.head();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Object element = currentHeadBlock.peek(head);
|
||||
if (element == REMOVED_ELEMENT)
|
||||
{
|
||||
// Already removed, try next index
|
||||
++head;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (T)element;
|
||||
if (result != null)
|
||||
{
|
||||
if (currentHeadBlock.remove(head, result, true))
|
||||
{
|
||||
// Item removed
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Concurrent remove, try next index
|
||||
++head;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue is empty
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHeadBlock(initialHeadBlock, currentHeadBlock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateHeadBlock(Block<T> oldHeadBlock, Block<T> newHeadBlock)
|
||||
{
|
||||
// Update the head block pointer if needs to
|
||||
if (oldHeadBlock != newHeadBlock)
|
||||
{
|
||||
// The head block pointer lagged behind.
|
||||
// If this update fails, it means that other threads
|
||||
// have emptied this block and pointed to a new one.
|
||||
casHeadBlock(oldHeadBlock, newHeadBlock);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean casHeadBlock(Block<T> current, Block<T> update)
|
||||
{
|
||||
return _blocks.compareAndSet(HEAD_OFFSET,current,update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T peek()
|
||||
{
|
||||
Block<T> currentHeadBlock = getHeadBlock();
|
||||
int head = currentHeadBlock.head();
|
||||
while (true)
|
||||
{
|
||||
if (head == getBlockSize())
|
||||
{
|
||||
Block<T> nextHeadBlock = currentHeadBlock.next();
|
||||
if (nextHeadBlock == null)
|
||||
{
|
||||
// Queue is empty
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use next block and loop
|
||||
currentHeadBlock = nextHeadBlock;
|
||||
head = currentHeadBlock.head();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Object element = currentHeadBlock.peek(head);
|
||||
if (element == REMOVED_ELEMENT)
|
||||
{
|
||||
// Already removed, try next index
|
||||
++head;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)element;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
Block<T> currentHeadBlock = getHeadBlock();
|
||||
int head = currentHeadBlock.head();
|
||||
boolean result = false;
|
||||
while (true)
|
||||
{
|
||||
if (head == getBlockSize())
|
||||
{
|
||||
Block<T> nextHeadBlock = currentHeadBlock.next();
|
||||
if (nextHeadBlock == null)
|
||||
{
|
||||
// Not found
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use next block and loop
|
||||
currentHeadBlock = nextHeadBlock;
|
||||
head = currentHeadBlock.head();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Object element = currentHeadBlock.peek(head);
|
||||
if (element == REMOVED_ELEMENT)
|
||||
{
|
||||
// Removed, try next index
|
||||
++head;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
// Not found
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.equals(o))
|
||||
{
|
||||
// Found
|
||||
if (currentHeadBlock.remove(head, o, false))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
++head;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not the one we're looking for
|
||||
++head;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c)
|
||||
{
|
||||
// TODO: super invocations are based on iterator.remove(), which throws
|
||||
return super.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c)
|
||||
{
|
||||
// TODO: super invocations are based on iterator.remove(), which throws
|
||||
return super.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
final List<Object[]> blocks = new ArrayList<>();
|
||||
Block<T> currentHeadBlock = getHeadBlock();
|
||||
while (currentHeadBlock != null)
|
||||
{
|
||||
Object[] elements = currentHeadBlock.arrayCopy();
|
||||
blocks.add(elements);
|
||||
currentHeadBlock = currentHeadBlock.next();
|
||||
}
|
||||
return new Iterator<T>()
|
||||
{
|
||||
private int blockIndex;
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (blockIndex == blocks.size())
|
||||
return false;
|
||||
|
||||
Object element = blocks.get(blockIndex)[index];
|
||||
|
||||
if (element == null)
|
||||
return false;
|
||||
|
||||
if (element != REMOVED_ELEMENT)
|
||||
return true;
|
||||
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (blockIndex == blocks.size())
|
||||
throw new NoSuchElementException();
|
||||
|
||||
Object element = blocks.get(blockIndex)[index];
|
||||
|
||||
if (element == null)
|
||||
throw new NoSuchElementException();
|
||||
|
||||
advance();
|
||||
|
||||
if (element != REMOVED_ELEMENT)
|
||||
return (T)element;
|
||||
}
|
||||
}
|
||||
|
||||
private void advance()
|
||||
{
|
||||
if (++index == getBlockSize())
|
||||
{
|
||||
index = 0;
|
||||
++blockIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
Block<T> currentHeadBlock = getHeadBlock();
|
||||
int head = currentHeadBlock.head();
|
||||
int size = 0;
|
||||
while (true)
|
||||
{
|
||||
if (head == getBlockSize())
|
||||
{
|
||||
Block<T> nextHeadBlock = currentHeadBlock.next();
|
||||
if (nextHeadBlock == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use next block and loop
|
||||
currentHeadBlock = nextHeadBlock;
|
||||
head = currentHeadBlock.head();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Object element = currentHeadBlock.peek(head);
|
||||
if (element == REMOVED_ELEMENT)
|
||||
{
|
||||
// Already removed, try next index
|
||||
++head;
|
||||
}
|
||||
else if (element != null)
|
||||
{
|
||||
++size;
|
||||
++head;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
protected Block<T> newBlock()
|
||||
{
|
||||
return new Block<>(getBlockSize());
|
||||
}
|
||||
|
||||
protected int getBlockCount()
|
||||
{
|
||||
int result = 0;
|
||||
Block<T> headBlock = getHeadBlock();
|
||||
while (headBlock != null)
|
||||
{
|
||||
++result;
|
||||
headBlock = headBlock.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static final class Block<E>
|
||||
{
|
||||
private static final int headOffset = MemoryUtils.getIntegersPerCacheLine()-1;
|
||||
private static final int tailOffset = MemoryUtils.getIntegersPerCacheLine()*2-1;
|
||||
|
||||
private final AtomicReferenceArray<Object> elements;
|
||||
private final AtomicReference<Block<E>> next = new AtomicReference<>();
|
||||
private final AtomicIntegerArray indexes = new AtomicIntegerArray(TAIL_OFFSET+1);
|
||||
|
||||
protected Block(int blockSize)
|
||||
{
|
||||
elements = new AtomicReferenceArray<>(blockSize);
|
||||
}
|
||||
|
||||
public Object peek(int index)
|
||||
{
|
||||
return elements.get(index);
|
||||
}
|
||||
|
||||
public boolean store(int index, E item)
|
||||
{
|
||||
boolean result = elements.compareAndSet(index, null, item);
|
||||
if (result)
|
||||
indexes.incrementAndGet(tailOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean remove(int index, Object item, boolean updateHead)
|
||||
{
|
||||
boolean result = elements.compareAndSet(index, item, REMOVED_ELEMENT);
|
||||
if (result && updateHead)
|
||||
indexes.incrementAndGet(headOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Block<E> next()
|
||||
{
|
||||
return next.get();
|
||||
}
|
||||
|
||||
public boolean link(Block<E> nextBlock)
|
||||
{
|
||||
return next.compareAndSet(null, nextBlock);
|
||||
}
|
||||
|
||||
public int head()
|
||||
{
|
||||
return indexes.get(headOffset);
|
||||
}
|
||||
|
||||
public int tail()
|
||||
{
|
||||
return indexes.get(tailOffset);
|
||||
}
|
||||
|
||||
public Object[] arrayCopy()
|
||||
{
|
||||
Object[] result = new Object[elements.length()];
|
||||
for (int i = 0; i < result.length; ++i)
|
||||
result[i] = elements.get(i);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* {@link MemoryUtils} provides an abstraction over memory properties and operations.
|
||||
* <p />
|
||||
*/
|
||||
public class MemoryUtils
|
||||
{
|
||||
private static final int cacheLineBytes;
|
||||
static
|
||||
{
|
||||
final int defaultValue = 64;
|
||||
int value = defaultValue;
|
||||
try
|
||||
{
|
||||
value = Integer.parseInt(AccessController.doPrivileged(new PrivilegedAction<String>()
|
||||
{
|
||||
@Override
|
||||
public String run()
|
||||
{
|
||||
return System.getProperty("org.eclipse.jetty.util.cacheLineBytes", String.valueOf(defaultValue));
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
}
|
||||
cacheLineBytes = value;
|
||||
}
|
||||
|
||||
private MemoryUtils()
|
||||
{
|
||||
}
|
||||
|
||||
public static int getCacheLineBytes()
|
||||
{
|
||||
return cacheLineBytes;
|
||||
}
|
||||
|
||||
public static int getIntegersPerCacheLine()
|
||||
{
|
||||
return getCacheLineBytes() >> 2;
|
||||
}
|
||||
|
||||
public static int getLongsPerCacheLine()
|
||||
{
|
||||
return getCacheLineBytes() >> 3;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util.component;
|
||||
|
||||
import java.io.FileWriter;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** A LifeCycle Listener that writes state changes to a file.
|
||||
* <p>This can be used with the jetty.sh script to wait for successful startup.
|
||||
*/
|
||||
public class FileNoticeLifeCycleListener implements LifeCycle.Listener
|
||||
{
|
||||
Logger LOG = Log.getLogger(FileNoticeLifeCycleListener.class);
|
||||
|
||||
private final String _filename;
|
||||
|
||||
public FileNoticeLifeCycleListener(String filename)
|
||||
{
|
||||
_filename=filename;
|
||||
}
|
||||
|
||||
private void writeState(String action, LifeCycle lifecycle)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileWriter out = new FileWriter(_filename,true);
|
||||
out.append(action).append(" ").append(lifecycle.toString()).append("\n");
|
||||
out.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void lifeCycleStarting(LifeCycle event)
|
||||
{
|
||||
writeState("STARTING",event);
|
||||
}
|
||||
|
||||
public void lifeCycleStarted(LifeCycle event)
|
||||
{
|
||||
writeState("STARTED",event);
|
||||
}
|
||||
|
||||
public void lifeCycleFailure(LifeCycle event, Throwable cause)
|
||||
{
|
||||
writeState("FAILED",event);
|
||||
}
|
||||
|
||||
public void lifeCycleStopping(LifeCycle event)
|
||||
{
|
||||
writeState("STOPPING",event);
|
||||
}
|
||||
|
||||
public void lifeCycleStopped(LifeCycle event)
|
||||
{
|
||||
writeState("STOPPED",event);
|
||||
}
|
||||
}
|
|
@ -32,6 +32,9 @@ import java.util.List;
|
|||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* A collection of resources (dirs).
|
||||
|
@ -45,6 +48,7 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
*/
|
||||
public class ResourceCollection extends Resource
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ResourceCollection.class);
|
||||
private Resource[] _resources;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -167,20 +171,25 @@ public class ResourceCollection extends Resource
|
|||
" argument must be a string containing one or more comma-separated resource strings.");
|
||||
}
|
||||
|
||||
_resources = new Resource[len];
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
for(int i=0; tokenizer.hasMoreTokens(); i++)
|
||||
while(tokenizer.hasMoreTokens())
|
||||
{
|
||||
_resources[i] = Resource.newResource(tokenizer.nextToken().trim());
|
||||
if(!_resources[i].exists() || !_resources[i].isDirectory())
|
||||
throw new IllegalArgumentException(_resources[i] + " is not an existing directory.");
|
||||
Resource resource = Resource.newResource(tokenizer.nextToken().trim());
|
||||
if(!resource.exists() || !resource.isDirectory())
|
||||
LOG.warn(" !exist "+resource);
|
||||
else
|
||||
resources.add(resource);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
_resources = resources.toArray(new Resource[resources.size()]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -194,7 +194,7 @@ public class SslContextFactory extends AbstractLifeCycle
|
|||
private SSLContext _context;
|
||||
|
||||
/** EndpointIdentificationAlgorithm - when set to "HTTPS" hostname verification will be enabled */
|
||||
private String _endpointIdentificationAlgorithm = "HTTPS";
|
||||
private String _endpointIdentificationAlgorithm = null;
|
||||
|
||||
private boolean _trustAll;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
|
|||
private final AtomicLong _lastShrink = new AtomicLong();
|
||||
private final ConcurrentLinkedQueue<Thread> _threads = new ConcurrentLinkedQueue<>();
|
||||
private final Object _joinLock = new Object();
|
||||
private BlockingQueue<Runnable> _jobs;
|
||||
private final BlockingQueue<Runnable> _jobs;
|
||||
private String _name = "qtp" + hashCode();
|
||||
private int _idleTimeout;
|
||||
private int _maxThreads;
|
||||
|
@ -79,11 +79,21 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
|
|||
}
|
||||
|
||||
public QueuedThreadPool(int maxThreads, int minThreads, int idleTimeout)
|
||||
{
|
||||
this(maxThreads, minThreads, 60000,null);
|
||||
}
|
||||
|
||||
public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue<Runnable> queue)
|
||||
{
|
||||
setMinThreads(minThreads);
|
||||
setMaxThreads(maxThreads);
|
||||
setIdleTimeout(idleTimeout);
|
||||
setStopTimeout(5000);
|
||||
|
||||
if (queue==null)
|
||||
queue=new BlockingArrayQueue<Runnable>(_minThreads, _minThreads);// TODO ConcurrentArrayBlockingQueue.Unbounded<Runnable>();
|
||||
_jobs=queue;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,9 +102,6 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
|
|||
super.doStart();
|
||||
_threadsStarted.set(0);
|
||||
|
||||
if (_jobs == null)
|
||||
setQueue(new BlockingArrayQueue<Runnable>(_minThreads, _minThreads));
|
||||
|
||||
startThreads(_minThreads);
|
||||
}
|
||||
|
||||
|
@ -602,7 +609,7 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
|
|||
*/
|
||||
public void setQueue(BlockingQueue<Runnable> queue)
|
||||
{
|
||||
_jobs = queue;
|
||||
throw new UnsupportedOperationException("Use constructor injection");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConcurrentArrayBlockingQueueUnboundedTest extends ConcurrentArrayQueueTest
|
||||
{
|
||||
@Rule
|
||||
public final TestTracker tracker = new TestTracker();
|
||||
|
||||
@Override
|
||||
protected ConcurrentArrayBlockingQueue<Integer> newConcurrentArrayQueue(int blockSize)
|
||||
{
|
||||
return new ConcurrentArrayBlockingQueue.Unbounded<>(blockSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferTake() throws Exception
|
||||
{
|
||||
ConcurrentArrayBlockingQueue<Integer> queue = newConcurrentArrayQueue(32);
|
||||
Integer item = 1;
|
||||
Assert.assertTrue(queue.offer(item));
|
||||
Integer result = queue.take();
|
||||
Assert.assertSame(item, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimedPollOffer() throws Exception
|
||||
{
|
||||
final ConcurrentArrayBlockingQueue<Integer> queue = newConcurrentArrayQueue(32);
|
||||
|
||||
final long timeout = 1000;
|
||||
final Integer item = 1;
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.MILLISECONDS.sleep(timeout);
|
||||
queue.offer(item);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
Integer result = queue.poll(2 * timeout, TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentOfferTake() throws Exception
|
||||
{
|
||||
final ConcurrentArrayBlockingQueue<Integer> queue = newConcurrentArrayQueue(512);
|
||||
int readerCount = 16;
|
||||
final int factor = 2;
|
||||
int writerCount = readerCount * factor;
|
||||
final int iterations = 4096;
|
||||
for (int runs = 0; runs < 16; ++runs)
|
||||
{
|
||||
ExecutorService executor = Executors.newFixedThreadPool(readerCount + writerCount);
|
||||
List<Future<Integer>> readers = new ArrayList<>();
|
||||
for (int i = 0; i < readerCount / 2; ++i)
|
||||
{
|
||||
final int reader = i;
|
||||
readers.add(executor.submit(new Callable<Integer>()
|
||||
{
|
||||
@Override
|
||||
public Integer call() throws Exception
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < iterations * factor; ++j)
|
||||
sum += queue.take();
|
||||
//System.err.println("Taking reader " + reader + " completed: " + sum);
|
||||
return sum;
|
||||
}
|
||||
}));
|
||||
readers.add(executor.submit(new Callable<Integer>()
|
||||
{
|
||||
@Override
|
||||
public Integer call() throws Exception
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < iterations * factor; ++j)
|
||||
sum += queue.poll(5, TimeUnit.SECONDS);
|
||||
//System.err.println("Polling Reader " + reader + " completed: " + sum);
|
||||
return sum;
|
||||
}
|
||||
}));
|
||||
}
|
||||
for (int i = 0; i < writerCount; ++i)
|
||||
{
|
||||
final int writer = i;
|
||||
executor.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
{
|
||||
for (int j = 0; j < iterations; ++j)
|
||||
queue.offer(1);
|
||||
//System.err.println("Writer " + writer + " completed");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
for (Future<Integer> result : readers)
|
||||
sum += result.get();
|
||||
|
||||
Assert.assertEquals(writerCount * iterations, sum);
|
||||
Assert.assertTrue(queue.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrain() throws Exception
|
||||
{
|
||||
final ConcurrentArrayBlockingQueue<Integer> queue = newConcurrentArrayQueue(512);
|
||||
List<Integer> chunk1 = Arrays.asList(1, 2);
|
||||
List<Integer> chunk2 = Arrays.asList(3, 4, 5);
|
||||
queue.addAll(chunk1);
|
||||
queue.addAll(chunk2);
|
||||
|
||||
List<Integer> drainer1 = new ArrayList<>();
|
||||
queue.drainTo(drainer1, chunk1.size());
|
||||
List<Integer> drainer2 = new ArrayList<>();
|
||||
queue.drainTo(drainer2, chunk2.size());
|
||||
|
||||
Assert.assertEquals(chunk1, drainer1);
|
||||
Assert.assertEquals(chunk2, drainer2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConcurrentArrayQueueTest
|
||||
{
|
||||
protected ConcurrentArrayQueue<Integer> newConcurrentArrayQueue(int blockSize)
|
||||
{
|
||||
return new ConcurrentArrayQueue<>(blockSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferCreatesBlock()
|
||||
{
|
||||
int blockSize = 2;
|
||||
ConcurrentArrayQueue<Integer> queue = newConcurrentArrayQueue(blockSize);
|
||||
int blocks = 3;
|
||||
for (int i = 0; i < blocks * blockSize + 1; ++i)
|
||||
queue.offer(i);
|
||||
Assert.assertEquals(blocks + 1, queue.getBlockCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeekRemove() throws Exception
|
||||
{
|
||||
int blockSize = 2;
|
||||
ConcurrentArrayQueue<Integer> queue = newConcurrentArrayQueue(blockSize);
|
||||
|
||||
Assert.assertNull(queue.peek());
|
||||
|
||||
queue.offer(1);
|
||||
queue.remove(1);
|
||||
Assert.assertNull(queue.peek());
|
||||
|
||||
int blocks = 3;
|
||||
int size = blocks * blockSize + 1;
|
||||
for (int i = 0; i < size; ++i)
|
||||
queue.offer(i);
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
Assert.assertEquals(i, (int)queue.peek());
|
||||
Assert.assertEquals(i, (int)queue.remove());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveObject() throws Exception
|
||||
{
|
||||
int blockSize = 2;
|
||||
ConcurrentArrayQueue<Integer> queue = newConcurrentArrayQueue(blockSize);
|
||||
queue.add(1);
|
||||
queue.add(2);
|
||||
queue.add(3);
|
||||
|
||||
Assert.assertFalse(queue.remove(4));
|
||||
|
||||
int size = queue.size();
|
||||
|
||||
Assert.assertTrue(queue.remove(2));
|
||||
--size;
|
||||
Assert.assertEquals(size, queue.size());
|
||||
|
||||
Iterator<Integer> iterator = queue.iterator();
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(1, (int)iterator.next());
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(3, (int)iterator.next());
|
||||
|
||||
queue.offer(4);
|
||||
++size;
|
||||
|
||||
Assert.assertTrue(queue.remove(3));
|
||||
--size;
|
||||
Assert.assertEquals(size, queue.size());
|
||||
|
||||
iterator = queue.iterator();
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(1, (int)iterator.next());
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(4, (int)iterator.next());
|
||||
|
||||
Assert.assertTrue(queue.remove(1));
|
||||
--size;
|
||||
Assert.assertTrue(queue.remove(4));
|
||||
--size;
|
||||
|
||||
iterator = queue.iterator();
|
||||
Assert.assertFalse(iterator.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() throws Exception
|
||||
{
|
||||
int blockSize = 2;
|
||||
ConcurrentArrayQueue<Integer> queue = newConcurrentArrayQueue(blockSize);
|
||||
queue.offer(1);
|
||||
Assert.assertEquals(1, queue.size());
|
||||
|
||||
queue = newConcurrentArrayQueue(blockSize);
|
||||
for (int i = 0; i < 2 * blockSize; ++i)
|
||||
queue.offer(i);
|
||||
for (int i = 0; i < blockSize; ++i)
|
||||
queue.poll();
|
||||
Assert.assertEquals(blockSize, queue.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() throws Exception
|
||||
{
|
||||
int blockSize = 2;
|
||||
ConcurrentArrayQueue<Integer> queue = newConcurrentArrayQueue(blockSize);
|
||||
queue.offer(1);
|
||||
Iterator<Integer> iterator = queue.iterator();
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(1, (int)iterator.next());
|
||||
Assert.assertFalse(iterator.hasNext());
|
||||
|
||||
try
|
||||
{
|
||||
iterator.next();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (NoSuchElementException ignored)
|
||||
{
|
||||
}
|
||||
|
||||
// Test block edge
|
||||
queue = newConcurrentArrayQueue(blockSize);
|
||||
for (int i = 0; i < blockSize * 2; ++i)
|
||||
queue.offer(i);
|
||||
queue.poll();
|
||||
iterator = queue.iterator();
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(1, (int)iterator.next());
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(2, (int)iterator.next());
|
||||
Assert.assertTrue(iterator.hasNext());
|
||||
Assert.assertEquals(3, (int)iterator.next());
|
||||
Assert.assertFalse(iterator.hasNext());
|
||||
|
||||
try
|
||||
{
|
||||
iterator.next();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (NoSuchElementException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -656,6 +656,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
/* ------------------------------------------------------------ */
|
||||
/** Add to the list of Server classes.
|
||||
* @see #setServerClasses(String[])
|
||||
* @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
|
||||
* @param classOrPackage A fully qualified class name (eg com.foo.MyClass)
|
||||
* or a qualified package name ending with '.' (eg com.foo.). If the class
|
||||
* or package has '-' it is excluded from the server classes and order is thus
|
||||
|
@ -673,6 +674,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
/* ------------------------------------------------------------ */
|
||||
/** Prepend to the list of Server classes.
|
||||
* @see #setServerClasses(String[])
|
||||
* @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
|
||||
* @param classOrPackage A fully qualified class name (eg com.foo.MyClass)
|
||||
* or a qualified package name ending with '.' (eg com.foo.). If the class
|
||||
* or package has '-' it is excluded from the server classes and order is thus
|
||||
|
@ -703,6 +705,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add to the list of System classes.
|
||||
* @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
|
||||
* @see #setSystemClasses(String[])
|
||||
* @param classOrPackage A fully qualified class name (eg com.foo.MyClass)
|
||||
* or a qualified package name ending with '.' (eg com.foo.). If the class
|
||||
|
@ -722,6 +725,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
/* ------------------------------------------------------------ */
|
||||
/** Prepend to the list of System classes.
|
||||
* @see #setSystemClasses(String[])
|
||||
* @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
|
||||
* @param classOrPackage A fully qualified class name (eg com.foo.MyClass)
|
||||
* or a qualified package name ending with '.' (eg com.foo.). If the class
|
||||
* or package has '-' it is excluded from the system classes and order is thus
|
||||
|
@ -872,7 +876,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
* @return True if the classloader should delegate first to the parent
|
||||
* classloader (standard java behaviour) or false if the classloader
|
||||
* should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
|
||||
* spec recommendation).
|
||||
* spec recommendation). Default is false or can be set by the system
|
||||
* property org.eclipse.jetty.server.webapp.parentLoaderPriority
|
||||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value="parent classloader given priority", readonly=true)
|
||||
|
@ -1104,7 +1109,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param java2compliant The java2compliant to set.
|
||||
* @param java2compliant True if the classloader should delegate first to the parent
|
||||
* classloader (standard java behaviour) or false if the classloader
|
||||
* should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
|
||||
* spec recommendation). Default is false or can be set by the system
|
||||
* property org.eclipse.jetty.server.webapp.parentLoaderPriority
|
||||
*/
|
||||
public void setParentLoaderPriority(boolean java2compliant)
|
||||
{
|
||||
|
|
|
@ -496,7 +496,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
@Override
|
||||
protected boolean onReadTimeout()
|
||||
{
|
||||
LOG.warn("Read Timeout");
|
||||
LOG.info("Read Timeout");
|
||||
|
||||
IOState state = getIOState();
|
||||
if ((state.getState() == ConnectionState.CLOSING) || (state.getState() == ConnectionState.CLOSED))
|
||||
|
|
Loading…
Reference in New Issue