- * Defaults to 302 Found
+ * Defaults to {@code 302 Found}
*/
public class RedirectRegexRule extends RegexRule
{
@@ -43,9 +42,9 @@ public class RedirectRegexRule extends RegexRule
public RedirectRegexRule()
{
- this(null,null);
+ this(null, null);
}
-
+
public RedirectRegexRule(@Name("regex") String regex, @Name("location") String location)
{
super(regex);
@@ -54,65 +53,63 @@ public class RedirectRegexRule extends RegexRule
setLocation(location);
}
+ /**
+ * @param replacement the URI to redirect to
+ * @deprecated use {@link #setLocation(String)} instead.
+ */
@Deprecated
public void setReplacement(String replacement)
{
- _location = replacement;
+ setLocation(replacement);
}
-
+
+ /**
+ * Sets the redirect location.
+ *
+ * @param location the URI to redirect to
+ */
public void setLocation(String location)
{
_location = location;
}
-
+
/**
* Sets the redirect status code.
- *
+ *
* @param statusCode the 3xx redirect status code
*/
public void setStatusCode(int statusCode)
{
- if ((300 <= statusCode) || (statusCode >= 399))
- {
+ if (statusCode >= 300 && statusCode <= 399)
_statusCode = statusCode;
- }
else
- {
throw new IllegalArgumentException("Invalid redirect status code " + statusCode + " (must be a value between 300 and 399)");
- }
}
-
+
@Override
- protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher)
- throws IOException
+ protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher) throws IOException
{
- target=_location;
- for (int g=1;g<=matcher.groupCount();g++)
+ target = _location;
+ for (int g = 1; g <= matcher.groupCount(); g++)
{
String group = matcher.group(g);
- target=target.replaceAll("\\$"+g,group);
+ target = target.replaceAll("\\$" + g, group);
}
-
+
target = response.encodeRedirectURL(target);
- response.setHeader("Location",RedirectUtil.toRedirectURL(request,target));
+ response.setHeader("Location", RedirectUtil.toRedirectURL(request, target));
response.setStatus(_statusCode);
response.getOutputStream().flush(); // no output / content
response.getOutputStream().close();
return target;
}
-
+
/**
* Returns the redirect status code and replacement.
*/
@Override
public String toString()
{
- StringBuilder str = new StringBuilder();
- str.append(super.toString());
- str.append('[').append(_statusCode);
- str.append('>').append(_location);
- str.append(']');
- return str.toString();
+ return String.format("%s[%d>%s]", super.toString(), _statusCode, _location);
}
-
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java
index de4e26718e0..e9f9f294a98 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java
@@ -24,7 +24,22 @@ import java.util.concurrent.atomic.AtomicLong;
* An AtomicLong with additional methods to treat it as two hi/lo integers.
*/
public class AtomicBiInteger extends AtomicLong
-{
+{
+
+ public AtomicBiInteger()
+ {
+ }
+
+ public AtomicBiInteger(long encoded)
+ {
+ super(encoded);
+ }
+
+ public AtomicBiInteger(int hi, int lo)
+ {
+ super(encode(hi, lo));
+ }
+
/**
* @return the hi value
*/
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java
deleted file mode 100644
index 6ecec73d382..00000000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java
+++ /dev/null
@@ -1,175 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2019 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.concurrent.atomic.AtomicLong;
-
-/**
- * An AtomicLong with additional methods to treat it as three 21 bit unsigned words.
- */
-public class AtomicTriInteger extends AtomicLong
-{
- public static int MAX_VALUE = 0x1FFFFF;
- public static int MIN_VALUE = 0;
-
- /**
- * Sets the hi and lo values.
- *
- * @param w0 the 0th word
- * @param w1 the 1st word
- * @param w2 the 2nd word
- */
- public void set(int w0, int w1, int w2)
- {
- set(encode(w0, w1, w2));
- }
-
- /**
- * Atomically sets the word values to the given updated values only if
- * the current encoded value is as expected.
- *
- * @param expectEncoded the expected encoded value
- * @param w0 the 0th word
- * @param w1 the 1st word
- * @param w2 the 2nd word
- * @return {@code true} if successful.
- */
- public boolean compareAndSet(long expectEncoded, int w0, int w1, int w2)
- {
- return compareAndSet(expectEncoded, encode(w0, w1, w2));
- }
-
- /**
- * Atomically adds the given deltas to the current hi and lo values.
- *
- * @param delta0 the delta to apply to the 0th word value
- * @param delta1 the delta to apply to the 1st word value
- * @param delta2 the delta to apply to the 2nd word value
- */
- public void add(int delta0, int delta1, int delta2)
- {
- while (true)
- {
- long encoded = get();
- long update = encode(
- getWord0(encoded) + delta0,
- getWord1(encoded) + delta1,
- getWord2(encoded) + delta2);
- if (compareAndSet(encoded, update))
- return;
- }
- }
-
- /**
- * Gets word 0 value
- *
- * @return the 16 bit value as an int
- */
- public int getWord0()
- {
- return getWord0(get());
- }
-
-
- /**
- * Gets word 1 value
- *
- * @return the 16 bit value as an int
- */
- public int getWord1()
- {
- return getWord1(get());
- }
-
- /**
- * Gets word 2 value
- *
- * @return the 16 bit value as an int
- */
- public int getWord2()
- {
- return getWord2(get());
- }
-
- /**
- * Gets word 0 value from the given encoded value.
- *
- * @param encoded the encoded value
- * @return the 16 bit value as an int
- */
- public static int getWord0(long encoded)
- {
- return (int)((encoded >> 42) & MAX_VALUE);
- }
-
- /**
- * Gets word 0 value from the given encoded value.
- *
- * @param encoded the encoded value
- * @return the 16 bit value as an int
- */
- public static int getWord1(long encoded)
- {
- return (int)((encoded >> 21) & MAX_VALUE);
- }
-
- /**
- * Gets word 0 value from the given encoded value.
- *
- * @param encoded the encoded value
- * @return the 16 bit value as an int
- */
- public static int getWord2(long encoded)
- {
- return (int)(encoded & MAX_VALUE);
- }
-
- /**
- * Encodes 4 16 bit words values into a long.
- *
- * @param w0 the 0th word
- * @param w1 the 1st word
- * @param w2 the 2nd word
- * @return the encoded value
- */
- public static long encode(int w0, int w1, int w2)
- {
- if (w0 < MIN_VALUE
- || w0 > MAX_VALUE
- || w1 < MIN_VALUE
- || w1 > MAX_VALUE
- || w2 < MIN_VALUE
- || w2 > MAX_VALUE)
- throw new IllegalArgumentException(String.format("Words must be %d <= word <= %d: %d, %d, %d", MIN_VALUE, MAX_VALUE, w0, w1, w2));
- long wl0 = ((long)w0) & MAX_VALUE;
- long wl1 = ((long)w1) & MAX_VALUE;
- long wl2 = ((long)w2) & MAX_VALUE;
- return (wl0 << 42) + (wl1 << 21) + (wl2);
- }
-
- @Override
- public String toString()
- {
- long encoded = get();
- int w0 = getWord0(encoded);
- int w1 = getWord1(encoded);
- int w2 = getWord2(encoded);
- return String.format("{%d,%d,%d}", w0, w1, w2);
- }
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
index 4658e88a27f..50596c8fac3 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
@@ -484,13 +484,56 @@ public class BlockingArrayQueue extends AbstractList implements BlockingQu
@Override
public int drainTo(Collection super E> c)
{
- throw new UnsupportedOperationException();
+ return drainTo(c, Integer.MAX_VALUE);
}
@Override
public int drainTo(Collection super E> c, int maxElements)
{
- throw new UnsupportedOperationException();
+ int elements = 0;
+ _tailLock.lock();
+ try
+ {
+ _headLock.lock();
+ try
+ {
+ final int head = _indexes[HEAD_OFFSET];
+ final int tail = _indexes[TAIL_OFFSET];
+ final int capacity = _elements.length;
+
+ int i = head;
+ while (i!=tail && elements extends AbstractList implements BlockingQu
_tailLock.lock();
try
{
-
_headLock.lock();
try
{
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
index 7b7253c9e29..c9da93cdb64 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -63,7 +64,12 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool
public ExecutorThreadPool(int maxThreads, int minThreads)
{
- this(new ThreadPoolExecutor(maxThreads, maxThreads, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()), minThreads, -1, null);
+ this(maxThreads, minThreads, new LinkedBlockingQueue<>());
+ }
+
+ public ExecutorThreadPool(int maxThreads, int minThreads, BlockingQueue queue)
+ {
+ this(new ThreadPoolExecutor(maxThreads, maxThreads, 60, TimeUnit.SECONDS, queue), minThreads, -1, null);
}
public ExecutorThreadPool(ThreadPoolExecutor executor)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
old mode 100755
new mode 100644
index 7d11e8e19e5..94dab54c6db
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
@@ -29,7 +29,7 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
-import org.eclipse.jetty.util.AtomicTriInteger;
+import org.eclipse.jetty.util.AtomicBiInteger;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -48,15 +48,16 @@ import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadPool, Dumpable, TryExecutor
{
private static final Logger LOG = Log.getLogger(QueuedThreadPool.class);
+ private static Runnable NOOP = () -> {};
/**
- * Encodes thread counts:
- *
Word0
Total thread count (including starting and idle)
- *
Word1
Starting threads
- *
Word2
Idle threads
+ * Encodes thread counts:
+ *
+ *
Hi
Total thread count or Integer.MIN_VALUE if stopping
+ *
Lo
Net idle threads == idle threads - job queue size
*
*/
- private final AtomicTriInteger _counts = new AtomicTriInteger();
+ private final AtomicBiInteger _counts = new AtomicBiInteger(Integer.MIN_VALUE, 0);
private final AtomicLong _lastShrink = new AtomicLong();
private final Set _threads = ConcurrentHashMap.newKeySet();
private final Object _joinLock = new Object();
@@ -84,12 +85,17 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
this(maxThreads, Math.min(8, maxThreads));
}
- public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads)
+ public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads)
{
this(maxThreads, minThreads, 60000);
}
- public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout")int idleTimeout)
+ public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("queue") BlockingQueue queue)
+ {
+ this(maxThreads, minThreads, 60000, -1, queue, null);
+ }
+
+ public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout)
{
this(maxThreads, minThreads, idleTimeout, null);
}
@@ -103,26 +109,24 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
{
this(maxThreads, minThreads, idleTimeout, -1, queue, threadGroup);
}
-
+
public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("reservedThreads") int reservedThreads, @Name("queue") BlockingQueue queue, @Name("threadGroup") ThreadGroup threadGroup)
{
- if (maxThreads < minThreads) {
- throw new IllegalArgumentException("max threads ("+maxThreads+") less than min threads ("
- +minThreads+")");
- }
-
+ if (maxThreads < minThreads)
+ throw new IllegalArgumentException("max threads (" + maxThreads + ") less than min threads ("
+ + minThreads + ")");
setMinThreads(minThreads);
setMaxThreads(maxThreads);
setIdleTimeout(idleTimeout);
setStopTimeout(5000);
setReservedThreads(reservedThreads);
- if (queue==null)
+ if (queue == null)
{
- int capacity=Math.max(_minThreads, 8);
- queue=new BlockingArrayQueue<>(capacity, capacity);
+ int capacity = Math.max(_minThreads, 8) * 1024;
+ queue = new BlockingArrayQueue<>(capacity, capacity);
}
- _jobs=queue;
- _threadGroup=threadGroup;
+ _jobs = queue;
+ _threadGroup = threadGroup;
setThreadPoolBudget(new ThreadPoolBudget(this));
}
@@ -134,7 +138,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
public void setThreadPoolBudget(ThreadPoolBudget budget)
{
- if (budget!=null && budget.getSizedThreadPool()!=this)
+ if (budget != null && budget.getSizedThreadPool() != this)
throw new IllegalArgumentException();
_budget = budget;
}
@@ -142,21 +146,21 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@Override
protected void doStart() throws Exception
{
- if (_reservedThreads==0)
+ if (_reservedThreads == 0)
{
_tryExecutor = NO_TRY;
}
else
{
- ReservedThreadExecutor reserved = new ReservedThreadExecutor(this,_reservedThreads);
- reserved.setIdleTimeout(_idleTimeout,TimeUnit.MILLISECONDS);
+ ReservedThreadExecutor reserved = new ReservedThreadExecutor(this, _reservedThreads);
+ reserved.setIdleTimeout(_idleTimeout, TimeUnit.MILLISECONDS);
_tryExecutor = reserved;
}
addBean(_tryExecutor);
-
- super.doStart();
- _counts.set(0,0,0); // threads, starting, idle
+ super.doStart();
+ // The threads count set to MIN_VALUE is used to signal to Runners that the pool is stopped.
+ _counts.set(0, 0); // threads, idle
ensureThreads();
}
@@ -168,54 +172,59 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
removeBean(_tryExecutor);
_tryExecutor = TryExecutor.NO_TRY;
-
+
super.doStop();
+ // Signal the Runner threads that we are stopping
+ int threads = _counts.getAndSetHi(Integer.MIN_VALUE);
+
+ // If stop timeout try to gracefully stop
long timeout = getStopTimeout();
BlockingQueue jobs = getQueue();
-
- // If no stop timeout, clear job queue
- if (timeout <= 0)
- jobs.clear();
-
- // Fill job Q with noop jobs to wakeup idle
- Runnable noop = () -> {};
- for (int i = getThreads(); i-- > 0; )
- jobs.offer(noop);
-
- // try to let jobs complete naturally for half our stop time
- joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
-
- // If we still have threads running, get a bit more aggressive
-
- // interrupt remaining threads
- for (Thread thread : _threads)
+ if (timeout > 0)
{
- if (LOG.isDebugEnabled())
- LOG.debug("Interrupting {}", thread);
- thread.interrupt();
- }
-
- // wait again for the other half of our stop time
- joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
-
- Thread.yield();
- if (LOG.isDebugEnabled())
- {
- for (Thread unstopped : _threads)
+ // Fill the job queue with noop jobs to wakeup idle threads.
+ for (int i = 0; i < threads; ++i)
{
- StringBuilder dmp = new StringBuilder();
- for (StackTraceElement element : unstopped.getStackTrace())
- {
- dmp.append(System.lineSeparator()).append("\tat ").append(element);
- }
- LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString());
+ jobs.offer(NOOP);
+ }
+
+ // try to let jobs complete naturally for half our stop time
+ joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
+
+ // If we still have threads running, get a bit more aggressive
+
+ // interrupt remaining threads
+ for (Thread thread : _threads)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Interrupting {}", thread);
+ thread.interrupt();
+ }
+
+ // wait again for the other half of our stop time
+ joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
+
+ Thread.yield();
+ if (LOG.isDebugEnabled())
+ {
+ for (Thread unstopped : _threads)
+ {
+ StringBuilder dmp = new StringBuilder();
+ for (StackTraceElement element : unstopped.getStackTrace())
+ {
+ dmp.append(System.lineSeparator()).append("\tat ").append(element);
+ }
+ LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString());
+ }
+ }
+ else
+ {
+ for (Thread unstopped : _threads)
+ {
+ LOG.warn("{} Couldn't stop {}", this, unstopped);
+ }
}
- }
- else
- {
- for (Thread unstopped : _threads)
- LOG.warn("{} Couldn't stop {}",this,unstopped);
}
// Close any un-executed jobs
@@ -233,11 +242,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
LOG.warn(t);
}
}
- else if (job != noop)
+ else if (job != NOOP)
LOG.warn("Stopped without executing or closing {}", job);
}
- if (_budget!=null)
+ if (_budget != null)
_budget.reset();
synchronized (_joinLock)
@@ -259,7 +268,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
}
/**
- * Thread Pool should use Daemon Threading.
+ * Thread Pool should use Daemon Threading.
*
* @param daemon true to enable delegation
* @see Thread#setDaemon(boolean)
@@ -291,9 +300,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@Override
public void setMaxThreads(int maxThreads)
{
- if (maxThreadsAtomicTriInteger.MAX_VALUE)
- throw new IllegalArgumentException("maxThreads="+maxThreads);
- if (_budget!=null)
+ if (_budget != null)
_budget.check(maxThreads);
_maxThreads = maxThreads;
if (_minThreads > _maxThreads)
@@ -317,11 +324,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
if (isStarted())
ensureThreads();
}
-
+
/**
* Set the number of reserved threads.
*
- * @param reservedThreads number of reserved threads or -1 for heuristically determined
+ * @param reservedThreads number of reserved threads or -1 for heuristically determined
* @see #getReservedThreads
*/
public void setReservedThreads(int reservedThreads)
@@ -426,10 +433,10 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
{
return _priority;
}
-
+
/**
* Get the size of the job queue.
- *
+ *
* @return Number of jobs queued waiting for a thread
*/
@ManagedAttribute("size of the job queue")
@@ -458,7 +465,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
{
_detailedDump = detailedDump;
}
-
+
@ManagedAttribute("threshold at which the pool is low on threads")
public int getLowThreadsThreshold()
{
@@ -473,22 +480,55 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@Override
public void execute(Runnable job)
{
- if (!isRunning() || !_jobs.offer(job))
+ // Determine if we need to start a thread, use and idle thread or just queue this job
+ boolean startThread;
+ while (true)
{
- LOG.warn("{} rejected {}", this, job);
+ // Get the atomic counts
+ long counts = _counts.get();
+
+ // Get the number of threads started (might not yet be running)
+ int threads = AtomicBiInteger.getHi(counts);
+ if (threads == Integer.MIN_VALUE)
+ throw new RejectedExecutionException(job.toString());
+
+ // Get the number of truly idle threads. This count is reduced by the
+ // job queue size so that any threads that are idle but are about to take
+ // a job from the queue are not counted.
+ int idle = AtomicBiInteger.getLo(counts);
+
+ // Start a thread if we have insufficient idle threads to meet demand
+ // and we are not at max threads.
+ startThread = (idle <= 0 && threads < _maxThreads);
+
+ // The job will be run by an idle thread when available
+ if (!_counts.compareAndSet(counts, threads + (startThread ? 1 : 0), idle - 1))
+ continue;
+
+ break;
+ }
+
+ if (!_jobs.offer(job))
+ {
+ // reverse our changes to _counts.
+ if (addCounts(startThread ? -1 : 0, 1))
+ LOG.warn("{} rejected {}", this, job);
throw new RejectedExecutionException(job.toString());
}
+
if (LOG.isDebugEnabled())
- LOG.debug("queue {}",job);
- // Make sure there is at least one thread executing the job.
- ensureThreads();
+ LOG.debug("queue {} startThread={}", job, startThread);
+
+ // Start a thread if one was needed
+ if (startThread)
+ startThread();
}
@Override
public boolean tryExecute(Runnable task)
{
TryExecutor tryExecutor = _tryExecutor;
- return tryExecutor!=null && tryExecutor.tryExecute(task);
+ return tryExecutor != null && tryExecutor.tryExecute(task);
}
/**
@@ -500,11 +540,15 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
synchronized (_joinLock)
{
while (isRunning())
+ {
_joinLock.wait();
+ }
}
while (isStopping())
+ {
Thread.sleep(1);
+ }
}
/**
@@ -514,7 +558,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@ManagedAttribute("number of threads in the pool")
public int getThreads()
{
- return _counts.getWord0();
+ int threads = _counts.getHi();
+ return Math.max(0, threads);
}
/**
@@ -524,7 +569,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@ManagedAttribute("number of idle threads in the pool")
public int getIdleThreads()
{
- return _counts.getWord2();
+ int idle = _counts.getLo();
+ return Math.max(0, idle);
}
/**
@@ -536,7 +582,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
int reserved = _tryExecutor instanceof ReservedThreadExecutor ? ((ReservedThreadExecutor)_tryExecutor).getAvailable() : 0;
return getThreads() - getIdleThreads() - reserved;
}
-
+
/**
*
Returns whether this thread pool is low on threads.
*
The current formula is:
@@ -556,43 +602,63 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
private void ensureThreads()
{
- while (isRunning())
+ while (true)
{
long counts = _counts.get();
- int threads = AtomicTriInteger.getWord0(counts);
- int starting = AtomicTriInteger.getWord1(counts);
- int idle = AtomicTriInteger.getWord2(counts);
- int queue = getQueueSize();
+ int threads = AtomicBiInteger.getHi(counts);
+ if (threads == Integer.MIN_VALUE)
+ break;
- if (threads >= _maxThreads)
- break;
- if (threads >= _minThreads && (starting + idle) >= queue)
- break;
- if (!_counts.compareAndSet(counts, threads + 1, starting + 1, idle))
+ // If we have less than min threads
+ // OR insufficient idle threads to meet demand
+ int idle = AtomicBiInteger.getLo(counts);
+ if (threads < _minThreads || (idle < 0 && threads < _maxThreads))
+ {
+ // Then try to start a thread.
+ if (_counts.compareAndSet(counts, threads + 1, idle))
+ startThread();
+ // Otherwise continue to check state again.
continue;
-
- boolean started = false;
- try
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Starting thread {}",this);
-
- Thread thread = newThread(_runnable);
- thread.setDaemon(isDaemon());
- thread.setPriority(getThreadsPriority());
- thread.setName(_name + "-" + thread.getId());
- if (LOG.isDebugEnabled())
- LOG.debug("Starting {}", thread);
- _threads.add(thread);
- _lastShrink.set(System.nanoTime());
- thread.start();
- started = true;
- }
- finally
- {
- if (!started)
- _counts.add(-1,-1,0); // threads, starting, idle
}
+ break;
+ }
+ }
+
+ protected void startThread()
+ {
+ boolean started = false;
+ try
+ {
+ Thread thread = newThread(_runnable);
+ thread.setDaemon(isDaemon());
+ thread.setPriority(getThreadsPriority());
+ thread.setName(_name + "-" + thread.getId());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Starting {}", thread);
+ _threads.add(thread);
+ _lastShrink.set(System.nanoTime());
+ thread.start();
+ started = true;
+ }
+ finally
+ {
+ if (!started)
+ addCounts(-1, 0); // threads, idle
+ }
+ }
+
+ private boolean addCounts(int deltaThreads, int deltaIdle)
+ {
+ while (true)
+ {
+ long encoded = _counts.get();
+ int threads = AtomicBiInteger.getHi(encoded);
+ int idle = AtomicBiInteger.getLo(encoded);
+ if (threads == Integer.MIN_VALUE) // This is a marker that the pool is stopped.
+ return false;
+ long update = AtomicBiInteger.encode(threads + deltaThreads, idle + deltaIdle);
+ if (_counts.compareAndSet(encoded, update))
+ return true;
}
}
@@ -621,19 +687,19 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
knownMethod = "IDLE ";
break;
}
-
+
if ("reservedWait".equals(t.getMethodName()) && t.getClassName().endsWith("ReservedThread"))
{
knownMethod = "RESERVED ";
break;
}
-
+
if ("select".equals(t.getMethodName()) && t.getClassName().endsWith("SelectorProducer"))
{
knownMethod = "SELECTING ";
break;
}
-
+
if ("accept".equals(t.getMethodName()) && t.getClassName().contains("ServerConnector"))
{
knownMethod = "ACCEPTING ";
@@ -664,8 +730,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
}
else
{
- int p=thread.getPriority();
- threads.add(thread.getId() + " " + thread.getName() + " " + known + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (p==Thread.NORM_PRIORITY?"":(" prio="+p)));
+ int p = thread.getPriority();
+ threads.add(thread.getId() + " " + thread.getName() + " " + known + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (p == Thread.NORM_PRIORITY ? "" : (" prio=" + p)));
}
}
@@ -684,12 +750,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
public String toString()
{
long count = _counts.get();
- int threads = AtomicTriInteger.getWord0(count);
- int starting = AtomicTriInteger.getWord1(count);
- int idle = AtomicTriInteger.getWord2(count);
+ int threads = Math.max(0, AtomicBiInteger.getHi(count));
+ int idle = Math.max(0, AtomicBiInteger.getLo(count));
int queue = getQueueSize();
- return String.format("%s[%s]@%x{%s,%d<=%d<=%d,s=%d,i=%d,r=%d,q=%d}[%s]",
+ return String.format("%s[%s]@%x{%s,%d<=%d<=%d,i=%d,r=%d,q=%d}[%s]",
getClass().getSimpleName(),
_name,
hashCode(),
@@ -697,7 +762,6 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
getMinThreads(),
threads,
getMaxThreads(),
- starting,
idle,
getReservedThreads(),
queue,
@@ -768,7 +832,9 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
buf.append(thread.getId()).append(" ").append(thread.getName()).append(" ");
buf.append(thread.getState()).append(":").append(System.lineSeparator());
for (StackTraceElement element : thread.getStackTrace())
+ {
buf.append(" at ").append(element.toString()).append(System.lineSeparator());
+ }
return buf.toString();
}
}
@@ -777,102 +843,110 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
private class Runner implements Runnable
{
- @Override
- public void run()
- {
- boolean idle = false;
- Runnable job = null;
-
- try
- {
- job = _jobs.poll();
- idle = job==null;
- _counts.add(0,-1,idle?1:0); // threads, starting, idle
-
- if (LOG.isDebugEnabled())
- LOG.debug("Runner started with {} for {}", job, QueuedThreadPool.this);
-
- while (true)
- {
- if (job == null)
- {
- if (!idle)
- {
- idle = true;
- _counts.add(0,0,1); // threads, starting, idle
- }
-
- long idleTimeout = getIdleTimeout();
- job = idleJobPoll(idleTimeout);
-
- // maybe we should shrink?
- if (job == null && getThreads() > _minThreads && idleTimeout > 0)
- {
- long last = _lastShrink.get();
- long now = System.nanoTime();
- if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(idleTimeout))
- {
- if (_lastShrink.compareAndSet(last, now))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("shrinking {}", QueuedThreadPool.this);
- break;
- }
- }
- }
- }
-
- // run job
- if (job != null)
- {
- if (idle)
- {
- idle = false;
- _counts.add(0,0,-1); // threads, starting, idle
- }
-
- if (LOG.isDebugEnabled())
- LOG.debug("run {} in {}", job, QueuedThreadPool.this);
- runJob(job);
- if (LOG.isDebugEnabled())
- LOG.debug("ran {} in {}", job, QueuedThreadPool.this);
-
- // Clear interrupted status
- Thread.interrupted();
- }
-
- if (!isRunning())
- break;
-
- job = _jobs.poll();
- }
- }
- catch (InterruptedException e)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("interrupted {} in {}", job, QueuedThreadPool.this);
- LOG.ignore(e);
- }
- catch (Throwable e)
- {
- LOG.warn(String.format("Unexpected thread death: %s in %s", job, QueuedThreadPool.this), e);
- }
- finally
- {
- _counts.add(-1,0,idle?-1:0); // threads, starting, idle
- removeThread(Thread.currentThread());
- ensureThreads();
-
- if (LOG.isDebugEnabled())
- LOG.debug("Runner exited for {}", QueuedThreadPool.this);
- }
- }
-
private Runnable idleJobPoll(long idleTimeout) throws InterruptedException
{
if (idleTimeout <= 0)
return _jobs.take();
return _jobs.poll(idleTimeout, TimeUnit.MILLISECONDS);
}
+
+ @Override
+ public void run()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Runner started for {}", QueuedThreadPool.this);
+
+ Runnable job = null;
+
+ try
+ {
+ // All threads start idle (not yet taken a job)
+ if (!addCounts(0, 1))
+ return;
+
+ while (true)
+ {
+ // If we had a job, signal that we are idle again
+ if (job != null)
+ {
+ if (!addCounts(0, 1))
+ break;
+ }
+ // else check we are still running
+ else if (_counts.getHi() == Integer.MIN_VALUE)
+ {
+ break;
+ }
+
+ try
+ {
+ // Look for an immediately available job
+ job = _jobs.poll();
+ if (job == null)
+ {
+ // Wait for a job
+ long idleTimeout = getIdleTimeout();
+ job = idleJobPoll(idleTimeout);
+
+ // If still no job?
+ if (job == null)
+ {
+ // maybe we should shrink
+ if (getThreads() > _minThreads && idleTimeout > 0)
+ {
+ long last = _lastShrink.get();
+ long now = System.nanoTime();
+ if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(idleTimeout))
+ {
+ if (_lastShrink.compareAndSet(last, now))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("shrinking {}", QueuedThreadPool.this);
+ break;
+ }
+ }
+ }
+ // continue to try again
+ continue;
+ }
+ }
+
+ // run job
+ if (LOG.isDebugEnabled())
+ LOG.debug("run {} in {}", job, QueuedThreadPool.this);
+ runJob(job);
+ if (LOG.isDebugEnabled())
+ LOG.debug("ran {} in {}", job, QueuedThreadPool.this);
+
+ // Clear any interrupted status
+ Thread.interrupted();
+ }
+ catch (InterruptedException e)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("interrupted {} in {}", job, QueuedThreadPool.this);
+ LOG.ignore(e);
+ }
+ catch (Throwable e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
+ finally
+ {
+ Thread thread = Thread.currentThread();
+ removeThread(thread);
+
+ // Decrement the total thread count and the idle count if we had no job
+ addCounts(-1, job == null ? -1 : 0);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} exited for {}", thread, QueuedThreadPool.this);
+
+ // There is a chance that we shrunk just as a job was queued for us, so
+ // check again if we have sufficient threads to meet demand
+ ensureThreads();
+ }
+ }
}
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
old mode 100755
new mode 100644
index 0ad6b76bb86..a0c889d162c
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
@@ -21,7 +21,6 @@ package org.eclipse.jetty.util.thread;
import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -47,13 +46,13 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
public ScheduledExecutorScheduler()
{
this(null, false);
- }
+ }
public ScheduledExecutorScheduler(String name, boolean daemon)
{
- this (name,daemon, Thread.currentThread().getContextClassLoader());
+ this(name, daemon, Thread.currentThread().getContextClassLoader());
}
-
+
public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader)
{
this(name, daemon, threadFactoryClassLoader, null);
@@ -70,16 +69,12 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
@Override
protected void doStart() throws Exception
{
- scheduler = new ScheduledThreadPoolExecutor(1, new ThreadFactory()
+ scheduler = new ScheduledThreadPoolExecutor(1, r ->
{
- @Override
- public Thread newThread(Runnable r)
- {
- Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name);
- thread.setDaemon(daemon);
- thread.setContextClassLoader(classloader);
- return thread;
- }
+ Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name);
+ thread.setDaemon(daemon);
+ thread.setContextClassLoader(classloader);
+ return thread;
});
scheduler.setRemoveOnCancelPolicy(true);
super.doStart();
@@ -97,8 +92,8 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
public Task schedule(Runnable task, long delay, TimeUnit unit)
{
ScheduledThreadPoolExecutor s = scheduler;
- if (s==null)
- return ()->false;
+ if (s == null)
+ return () -> false;
ScheduledFuture> result = s.schedule(task, delay, unit);
return new ScheduledFutureTask(result);
}
@@ -116,7 +111,7 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
if (thread == null)
Dumpable.dumpObject(out, this);
else
- Dumpable.dumpObjects(out,indent,this, (Object[])thread.getStackTrace());
+ Dumpable.dumpObjects(out, indent, this, (Object[])thread.getStackTrace());
}
private static class ScheduledFutureTask implements Task
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java
deleted file mode 100644
index cd1bd5ade8c..00000000000
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2019 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 org.junit.jupiter.api.Test;
-
-import static org.eclipse.jetty.util.AtomicTriInteger.MAX_VALUE;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class AtomicTriIntegerTest
-{
-
- @Test
- public void testBitOperations()
- {
- long encoded;
-
- encoded = AtomicTriInteger.encode(0,0,0);
- assertThat(AtomicTriInteger.getWord0(encoded),is(0));
- assertThat(AtomicTriInteger.getWord1(encoded),is(0));
- assertThat(AtomicTriInteger.getWord2(encoded),is(0));
-
- encoded = AtomicTriInteger.encode(1,2,3);
- assertThat(AtomicTriInteger.getWord0(encoded),is(1));
- assertThat(AtomicTriInteger.getWord1(encoded),is(2));
- assertThat(AtomicTriInteger.getWord2(encoded),is(3));
-
- encoded = AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, MAX_VALUE);
- assertThat(AtomicTriInteger.getWord0(encoded),is(MAX_VALUE));
- assertThat(AtomicTriInteger.getWord1(encoded),is(MAX_VALUE));
- assertThat(AtomicTriInteger.getWord2(encoded),is(MAX_VALUE));
-
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(-1, MAX_VALUE, MAX_VALUE));
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, -1, MAX_VALUE));
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, -1));
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE+1, MAX_VALUE, MAX_VALUE));
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE+1, MAX_VALUE));
- assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, MAX_VALUE+1));
- }
-
- @Test
- public void testSetGet()
- {
- AtomicTriInteger ati = new AtomicTriInteger();
- ati.set(1,2,3);
- assertThat(ati.getWord0(),is(1));
- assertThat(ati.getWord1(),is(2));
- assertThat(ati.getWord2(),is(3));
- }
-
- @Test
- public void testCompareAndSet()
- {
- AtomicTriInteger ati = new AtomicTriInteger();
- ati.set(1,2,3);
- long value = ati.get();
-
- ati.set(2,3,4);
- assertFalse(ati.compareAndSet(value,5,6,7));
- assertThat(ati.getWord0(),is(2));
- assertThat(ati.getWord1(),is(3));
- assertThat(ati.getWord2(),is(4));
-
- value = ati.get();
- assertTrue(ati.compareAndSet(value,6,7,8));
- assertThat(ati.getWord0(),is(6));
- assertThat(ati.getWord1(),is(7));
- assertThat(ati.getWord2(),is(8));
- }
-
-
- @Test
- public void testAdd()
- {
- AtomicTriInteger ati = new AtomicTriInteger();
- ati.set(1,2,3);
- ati.add(-1,-2,4);
- assertThat(ati.getWord0(),is(0));
- assertThat(ati.getWord1(),is(0));
- assertThat(ati.getWord2(),is(7));
- }
-
-}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java
index 0d59a1ec4af..3a35ff6fb92 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java
@@ -18,12 +18,9 @@
package org.eclipse.jetty.util;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -31,10 +28,16 @@ import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-
+import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
public class BlockingArrayQueueTest
{
@Test
@@ -494,4 +497,28 @@ public class BlockingArrayQueueTest
assertTrue(iterator.hasNext());
assertFalse(iterator.hasPrevious());
}
+
+
+ @Test
+ public void testDrainTo() throws Exception
+ {
+ BlockingArrayQueue queue = new BlockingArrayQueue<>();
+ queue.add("one");
+ queue.add("two");
+ queue.add("three");
+ queue.add("four");
+ queue.add("five");
+ queue.add("six");
+
+ List to = new ArrayList<>();
+ queue.drainTo(to,3);
+ assertThat(to, Matchers.contains("one", "two", "three"));
+ assertThat(queue.size(),Matchers.is(3));
+ assertThat(queue, Matchers.contains("four", "five", "six"));
+
+ queue.drainTo(to);
+ assertThat(to, Matchers.contains("one", "two", "three", "four", "five", "six"));
+ assertThat(queue.size(),Matchers.is(0));
+ assertThat(queue, Matchers.empty());
+ }
}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
index 1f00ac25f9c..e03322d7b74 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
@@ -181,6 +181,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
RunningJob job4=new RunningJob("JOB4");
tp.execute(job4);
assertFalse(job4._run.await(1,TimeUnit.SECONDS));
+ assertThat(tp.getThreads(),is(4));
// finish job 0
job0._stopping.countDown();
@@ -214,19 +215,21 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
duration = System.nanoTime() - duration;
assertThat(TimeUnit.NANOSECONDS.toMillis(duration), Matchers.greaterThan(tp.getIdleTimeout()/2L));
assertThat(TimeUnit.NANOSECONDS.toMillis(duration), Matchers.lessThan(tp.getIdleTimeout()*2L));
+
+ tp.stop();
}
@Test
public void testThreadPoolFailingJobs() throws Exception
{
+ QueuedThreadPool tp= new QueuedThreadPool();
+ tp.setMinThreads(2);
+ tp.setMaxThreads(4);
+ tp.setIdleTimeout(900);
+ tp.setThreadsPriority(Thread.NORM_PRIORITY-1);
+
try (StacklessLogging stackless = new StacklessLogging(QueuedThreadPool.class))
{
- QueuedThreadPool tp= new QueuedThreadPool();
- tp.setMinThreads(2);
- tp.setMaxThreads(4);
- tp.setIdleTimeout(900);
- tp.setThreadsPriority(Thread.NORM_PRIORITY-1);
-
tp.start();
// min threads started
@@ -297,8 +300,10 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertTrue(job4._stopped.await(10,TimeUnit.SECONDS));
waitForIdle(tp,2);
- assertThat(tp.getThreads(),is(2));
+ waitForThreads(tp,2);
}
+
+ tp.stop();
}
@Test
@@ -340,6 +345,8 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
RunningJob job4 = new RunningJob();
tp.execute(job4);
assertTrue(job4._run.await(5, TimeUnit.SECONDS));
+
+ tp.stop();
}
@Test
@@ -364,7 +371,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
tp.execute(job0);
tp.execute(job1);
- // Add a more jobs (which should not be run)
+ // Add more jobs (which should not be run)
RunningJob job2=new RunningJob();
CloseableJob job3=new CloseableJob();
RunningJob job4=new RunningJob();
@@ -391,6 +398,8 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
// Verify ClosableJobs have not been run but have been closed
assertThat(job4._run.await(200, TimeUnit.MILLISECONDS), is(false));
assertThat(job3._closed.await(200, TimeUnit.MILLISECONDS), is(true));
+
+ tp.stop();
}
@@ -437,6 +446,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
}
waitForThreads(tp,2);
waitForIdle(tp,2);
+ tp.stop();
}
@Test
@@ -484,6 +494,27 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertEquals(idle, tp.getIdleThreads());
}
+
+ private void waitForReserved(QueuedThreadPool tp, int reserved)
+ {
+ long now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
+ long start=now;
+ ReservedThreadExecutor reservedThreadExecutor = tp.getBean(ReservedThreadExecutor.class);
+ while (reservedThreadExecutor.getAvailable()!=reserved && (now-start)<10000)
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch(InterruptedException ignored)
+ {
+ }
+ now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
+ }
+ assertEquals(reserved, reservedThreadExecutor.getAvailable());
+ }
+
+
private void waitForThreads(QueuedThreadPool tp, int threads)
{
long now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
@@ -520,6 +551,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
Thread.sleep(100);
assertThat(tp.getThreads(),greaterThanOrEqualTo(5));
}
+ tp.stop();
}
@Test
@@ -548,24 +580,26 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
public void testDump() throws Exception
{
QueuedThreadPool pool = new QueuedThreadPool(4, 3);
+ pool.setIdleTimeout(10000);
String dump = pool.dump();
// TODO use hamcrest 2.0 regex matcher
assertThat(dump,containsString("STOPPED"));
- assertThat(dump,containsString(",3<=0<=4,s=0,i=0,r=-1,q=0"));
+ assertThat(dump,containsString(",3<=0<=4,i=0,r=-1,q=0"));
assertThat(dump,containsString("[NO_TRY]"));
pool.setReservedThreads(2);
dump = pool.dump();
assertThat(dump,containsString("STOPPED"));
- assertThat(dump,containsString(",3<=0<=4,s=0,i=0,r=2,q=0"));
+ assertThat(dump,containsString(",3<=0<=4,i=0,r=2,q=0"));
assertThat(dump,containsString("[NO_TRY]"));
pool.start();
waitForIdle(pool,3);
+ Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle
dump = pool.dump();
assertThat(count(dump," - STARTED"),is(2));
- assertThat(dump,containsString(",3<=3<=4,s=0,i=3,r=2,q=0"));
+ assertThat(dump,containsString(",3<=3<=4,i=3,r=2,q=0"));
assertThat(dump,containsString("[ReservedThreadExecutor@"));
assertThat(count(dump," IDLE "),is(3));
assertThat(count(dump," RESERVED "),is(0));
@@ -585,10 +619,10 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
}
});
started.await();
-
+ Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle
dump = pool.dump();
assertThat(count(dump," - STARTED"),is(2));
- assertThat(dump,containsString(",3<=3<=4,s=0,i=2,r=2,q=0"));
+ assertThat(dump,containsString(",3<=3<=4,i=2,r=2,q=0"));
assertThat(dump,containsString("[ReservedThreadExecutor@"));
assertThat(count(dump," IDLE "),is(2));
assertThat(count(dump," WAITING "),is(1));
@@ -598,7 +632,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
pool.setDetailedDump(true);
dump = pool.dump();
assertThat(count(dump," - STARTED"),is(2));
- assertThat(dump,containsString(",3<=3<=4,s=0,i=2,r=2,q=0"));
+ assertThat(dump,containsString(",3<=3<=4,i=2,r=2,q=0"));
assertThat(dump,containsString("s=0/2"));
assertThat(dump,containsString("[ReservedThreadExecutor@"));
assertThat(count(dump," IDLE "),is(2));
@@ -607,12 +641,11 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertThat(count(dump,"QueuedThreadPoolTest.lambda$testDump$"),is(1));
assertFalse(pool.tryExecute(()->{}));
- while(pool.getIdleThreads()==2)
- Thread.sleep(10);
-
+ waitForReserved(pool,1);
+ Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle
dump = pool.dump();
assertThat(count(dump," - STARTED"),is(2));
- assertThat(dump,containsString(",3<=3<=4,s=0,i=1,r=2,q=0"));
+ assertThat(dump,containsString(",3<=3<=4,i=1,r=2,q=0"));
assertThat(dump,containsString("s=1/2"));
assertThat(dump,containsString("[ReservedThreadExecutor@"));
assertThat(count(dump," IDLE "),is(1));
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java
new file mode 100644
index 00000000000..0d94a8a35df
--- /dev/null
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java
@@ -0,0 +1,68 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.jsr356.decoders;
+
+import org.junit.jupiter.api.Test;
+
+import javax.websocket.DecodeException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for class {@link FloatDecoder}.
+ *
+ * @see FloatDecoder
+ */
+public class FloatDecoderTest
+{
+
+ @Test
+ public void testWillDecodeReturningTrue()
+ {
+ assertTrue(new FloatDecoder().willDecode("21"));
+ }
+
+ @Test
+ public void testWillDecodeReturningFalse()
+ {
+ assertFalse(new FloatDecoder().willDecode("NaN"));
+ }
+
+ @Test
+ public void testWillDecodeWithNull()
+ {
+ assertFalse(FloatDecoder.INSTANCE.willDecode(null));
+ }
+
+ @Test
+ public void testDecodeThrowsDecodeException()
+ {
+ assertThrows(DecodeException.class, () -> new FloatDecoder().decode("NaN"));
+ }
+
+ @Test
+ public void testDecode() throws DecodeException
+ {
+ assertEquals(4.1F, new FloatDecoder().decode("4.1"), 0.01F);
+ }
+
+}
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java
index 49c1f6afbb4..08f3765b289 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java
@@ -18,13 +18,14 @@
package org.eclipse.jetty.websocket.jsr356.decoders;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
+import org.junit.jupiter.api.Test;
import javax.websocket.DecodeException;
-
-import org.junit.jupiter.api.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class IntegerDecoderTest
{
@@ -33,6 +34,26 @@ public class IntegerDecoderTest
{
IntegerDecoder decoder = new IntegerDecoder();
Integer val = decoder.decode("123");
- assertThat("Decoded value",val,is(123));
+ assertThat("Decoded value", val, is(123));
}
+
+ @Test
+ public void testWillDecodeWithNull()
+ {
+ assertFalse(new IntegerDecoder().willDecode(null));
+ }
+
+ @Test
+ public void testWillDecodeWithNonEmptyString()
+ {
+ assertFalse(new IntegerDecoder().willDecode("a"));
+ }
+
+ @Test
+ public void testDecodeThrowsDecodeException()
+ {
+ assertThrows(DecodeException.class, () -> IntegerDecoder.INSTANCE.decode(""));
+
+ }
+
}
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java
new file mode 100644
index 00000000000..e100bf11fca
--- /dev/null
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java
@@ -0,0 +1,54 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.jsr356.decoders;
+
+import org.junit.jupiter.api.Test;
+
+import javax.websocket.DecodeException;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Unit tests for class {@link LongDecoder}.
+ *
+ * @see LongDecoder
+ */
+public class LongDecoderTest
+{
+
+ @Test
+ public void testCreatesLongDecoder()
+ {
+ assertFalse(new LongDecoder().willDecode(null));
+ }
+
+ @Test
+ public void testWillDecodeWithNonEmptyString()
+ {
+ assertFalse(LongDecoder.INSTANCE.willDecode("Unable to parse Long"));
+ }
+
+ @Test
+ public void testDecodeThrowsDecodeException()
+ {
+ assertThrows(DecodeException.class, () -> LongDecoder.INSTANCE.decode("Unable to parse Long"));
+ }
+
+}
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java
new file mode 100644
index 00000000000..764323ecafc
--- /dev/null
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java
@@ -0,0 +1,55 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.jsr356.decoders;
+
+import org.junit.jupiter.api.Test;
+
+import javax.websocket.DecodeException;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Unit tests for class {@link ShortDecoder}.
+ *
+ * @see ShortDecoder
+ */
+public class ShortDecoderTest
+{
+
+ @Test
+ public void testWillDecodeWithNull()
+ {
+ assertFalse(new ShortDecoder().willDecode(null));
+ }
+
+ @Test
+ public void testWillDecodeWithNonEmptyString()
+ {
+ assertFalse(new ShortDecoder().willDecode(".iix/PN}f[&- ShortDecoder.INSTANCE.decode("$Yta3*m*%"));
+
+ }
+
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java
index 83c23a91de6..d9c943a100b 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java
@@ -18,11 +18,13 @@
package org.eclipse.jetty.websocket.common.util;
+import org.eclipse.jetty.io.AbstractConnection;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Unit tests for class {@link ReflectUtils}.
@@ -68,6 +70,18 @@ public class ReflectUtilsTest
assertFalse(ReflectUtils.isDefaultConstructable(Integer.class));
}
+ @Test
+ public void testIsDefaultConstructableWithAbstractClass()
+ {
+ assertFalse(ReflectUtils.isDefaultConstructable(AbstractConnection.class));
+ }
+
+ @Test
+ public void testIsDefaultConstructableWithObjectClass()
+ {
+ assertTrue(ReflectUtils.isDefaultConstructable(Object.class));
+ }
+
@Test
public void testFindGenericClassForStringClassTwice()
{
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
index 09bcefc032d..a2a57075627 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
@@ -37,6 +37,7 @@ import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -68,26 +69,31 @@ import org.xml.sax.SAXException;
* {@link ConfigurationProcessorFactory} interface to be found by the
* {@link ServiceLoader} by using the DTD and first tag element in the file.
* Note that DTD will be null if validation is off.
- *
- * The configuration can be parameterised with properties that are looked up via the
- * Property XML element and set on the configuration via the map returned from
+ *
The configuration can be parameterised with properties that are looked up via the
+ * Property XML element and set on the configuration via the map returned from
* {@link #getProperties()}
- *
- * The configuration can create and lookup beans by ID. If multiple configurations are used, then it
- * is good practise to copy the entries from the {@link #getIdMap()} of a configuration to the next
+ *
The configuration can create and lookup beans by ID. If multiple configurations are used, then it
+ * is good practise to copy the entries from the {@link #getIdMap()} of a configuration to the next
* configuration so that they can share an ID space for beans.
@@ -127,6 +130,7 @@ public class XmlConfiguration
*
Property jetty.webapps
*
Property jetty.webapps.uri
*
+ *
* @param server The Server object to set
* @param webapp The webapps Resource
*/
@@ -134,18 +138,18 @@ public class XmlConfiguration
{
try
{
- if (server!=null)
+ if (server != null)
getIdMap().put("Server", server);
Path home = Paths.get(System.getProperty("jetty.home", "."));
- getProperties().put("jetty.home",home.toString());
- getProperties().put("jetty.home.uri",normalizeURI(home.toUri().toASCIIString()));
+ getProperties().put("jetty.home", home.toString());
+ getProperties().put("jetty.home.uri", normalizeURI(home.toUri().toASCIIString()));
Path base = Paths.get(System.getProperty("jetty.base", home.toString()));
- getProperties().put("jetty.base",base.toString());
- getProperties().put("jetty.base.uri",normalizeURI(base.toUri().toASCIIString()));
+ getProperties().put("jetty.base", base.toString());
+ getProperties().put("jetty.base.uri", normalizeURI(base.toUri().toASCIIString()));
- if (webapp!=null)
+ if (webapp != null)
{
Path webappPath = webapp.getFile().toPath().toAbsolutePath();
getProperties().put("jetty.webapp", webappPath.toString());
@@ -153,19 +157,19 @@ public class XmlConfiguration
getProperties().put("jetty.webapps.uri", normalizeURI(webapp.getURI().toString()));
}
}
- catch(Exception e)
+ catch (Exception e)
{
LOG.warn(e);
}
}
-
+
public static String normalizeURI(String uri)
{
if (uri.endsWith("/"))
- return uri.substring(0,uri.length()-1);
+ return uri.substring(0, uri.length() - 1);
return uri;
}
-
+
private final Map _idMap = new HashMap<>();
private final Map _propertyMap = new HashMap<>();
private final URL _url;
@@ -183,9 +187,9 @@ public class XmlConfiguration
{
synchronized (__parser)
{
- _url=configuration;
+ _url = configuration;
setConfig(__parser.parse(configuration.toString()));
- _dtd=__parser.getDTD();
+ _dtd = __parser.getDTD();
}
}
@@ -200,13 +204,13 @@ public class XmlConfiguration
public XmlConfiguration(String configuration) throws SAXException, IOException
{
configuration = "\n"
- + configuration;
+ + configuration;
InputSource source = new InputSource(new StringReader(configuration));
synchronized (__parser)
{
- _url=null;
- setConfig( __parser.parse(source));
- _dtd=__parser.getDTD();
+ _url = null;
+ setConfig(__parser.parse(source));
+ _dtd = __parser.getDTD();
}
}
@@ -222,9 +226,9 @@ public class XmlConfiguration
InputSource source = new InputSource(configuration);
synchronized (__parser)
{
- _url=null;
+ _url = null;
setConfig(__parser.parse(source));
- _dtd=__parser.getDTD();
+ _dtd = __parser.getDTD();
}
}
@@ -232,40 +236,39 @@ public class XmlConfiguration
{
if ("Configure".equals(config.getTag()))
{
- _processor=new JettyXmlConfiguration();
+ _processor = new JettyXmlConfiguration();
}
- else if (__factoryLoader!=null)
+ else if (__factoryLoader != null)
{
for (ConfigurationProcessorFactory factory : __factoryLoader)
{
_processor = factory.getConfigurationProcessor(_dtd, config.getTag());
- if (_processor!=null)
+ if (_processor != null)
break;
}
-
- if (_processor==null)
- throw new IllegalStateException("Unknown configuration type: "+config.getTag()+" in "+this);
+ if (_processor == null)
+ throw new IllegalStateException("Unknown configuration type: " + config.getTag() + " in " + this);
}
else
{
- throw new IllegalArgumentException("Unknown XML tag:"+config.getTag());
+ throw new IllegalArgumentException("Unknown XML tag:" + config.getTag());
}
- _processor.init(_url,config,this);
+ _processor.init(_url, config, this);
}
- /* ------------------------------------------------------------ */
- /** Get the map of ID String to Objects that is used to hold
- * and lookup any objects by ID.
+ /**
+ * Get the map of ID String to Objects that is used to hold
+ * and lookup any objects by ID.
*
* A New, Get or Call XML element may have an
* id attribute which will cause the resulting object to be placed into
* this map. A Ref XML element will lookup an object from this map.
*
- * When chaining configuration files, it is good practise to copy the
+ * When chaining configuration files, it is good practise to copy the
* ID entries from the ID map to the map of the next configuration, so
* that they may share an ID space
*
- *
+ *
* @return A modifiable map of ID strings to Objects
*/
public Map getIdMap()
@@ -273,10 +276,10 @@ public class XmlConfiguration
return _idMap;
}
- /* ------------------------------------------------------------ */
/**
* Get the map of properties used by the Property XML element
- * to parameterise configuration.
+ * to parametrize configuration.
+ *
* @return A modifiable map of properties.
*/
public Map getProperties()
@@ -289,8 +292,8 @@ public class XmlConfiguration
*
* @param obj The object to be configured, which must be of a type or super type
* of the class attribute of the <Configure> element.
- * @throws Exception if the configuration fails
* @return the configured object
+ * @throws Exception if the configuration fails
*/
public Object configure(Object obj) throws Exception
{
@@ -310,22 +313,21 @@ public class XmlConfiguration
{
return _processor.configure();
}
-
- /* ------------------------------------------------------------ */
- /** Initialize a new Object defaults.
- *
This method must be called by any {@link ConfigurationProcessor} when it
- * creates a new instance of an object before configuring it, so that a derived
+
+ /**
+ * Initialize a new Object defaults.
+ *
This method must be called by any {@link ConfigurationProcessor} when it
+ * creates a new instance of an object before configuring it, so that a derived
* XmlConfiguration class may inject default values.
+ *
* @param object the object to initialize defaults on
*/
public void initializeDefaults(Object object)
{
}
-
private static class JettyXmlConfiguration implements ConfigurationProcessor
{
-
private String _url;
XmlParser.Node _root;
XmlConfiguration _configuration;
@@ -333,9 +335,9 @@ public class XmlConfiguration
@Override
public void init(URL url, XmlParser.Node root, XmlConfiguration configuration)
{
- _url=url==null?null:url.toString();
- _root=root;
- _configuration=configuration;
+ _url = url == null ? null : url.toString();
+ _root = root;
+ _configuration = configuration;
}
@Override
@@ -345,13 +347,13 @@ public class XmlConfiguration
Class> oClass = nodeClass(_root);
if (oClass != null && !oClass.isInstance(obj))
{
- String loaders = (oClass.getClassLoader()==obj.getClass().getClassLoader())?"":"Object Class and type Class are from different loaders.";
- throw new IllegalArgumentException("Object of class '"+obj.getClass().getCanonicalName()+"' is not of type '" + oClass.getCanonicalName()+"'. "+loaders+" in "+_url);
+ String loaders = (oClass.getClassLoader() == obj.getClass().getClassLoader()) ? "" : "Object Class and type Class are from different loaders.";
+ throw new IllegalArgumentException("Object of class '" + obj.getClass().getCanonicalName() + "' is not of type '" + oClass.getCanonicalName() + "'. " + loaders + " in " + _url);
}
- String id=_root.getAttribute("id");
- if (id!=null)
- _configuration.getIdMap().put(id,obj);
- configure(obj,_root,0);
+ String id = _root.getAttribute("id");
+ if (id != null)
+ _configuration.getIdMap().put(id, obj);
+ configure(obj, _root, 0);
return obj;
}
@@ -361,7 +363,7 @@ public class XmlConfiguration
Class> oClass = nodeClass(_root);
String id = _root.getAttribute("id");
- Object obj = id == null?null:_configuration.getIdMap().get(id);
+ Object obj = id == null ? null : _configuration.getIdMap().get(id);
int index = 0;
if (obj == null && oClass != null)
@@ -374,23 +376,21 @@ public class XmlConfiguration
{
Object o = _root.get(i);
if (o instanceof String)
- {
continue;
- }
- XmlParser.Node node = (XmlParser.Node)o;
- if (!(node.getTag().equals("Arg")))
+ XmlParser.Node node = (XmlParser.Node)o;
+ if (node.getTag().equals("Arg"))
{
- index = i;
- break;
+ String namedAttribute = node.getAttribute("name");
+ Object value = value(null, (XmlParser.Node)o);
+ if (namedAttribute != null)
+ namedArgMap.put(namedAttribute, value);
+ arguments.add(value);
}
else
{
- String namedAttribute = node.getAttribute("name");
- Object value=value(obj,(XmlParser.Node)o);
- if (namedAttribute != null)
- namedArgMap.put(namedAttribute,value);
- arguments.add(value);
+ index = i;
+ break;
}
}
@@ -403,12 +403,12 @@ public class XmlConfiguration
}
catch (NoSuchMethodException x)
{
- throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s",oClass,arguments,namedArgMap,_url));
+ throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s", oClass, arguments, namedArgMap, _url));
}
}
- if (id!=null)
- _configuration.getIdMap().put(id,obj);
-
+ if (id != null)
+ _configuration.getIdMap().put(id, obj);
+
_configuration.initializeDefaults(obj);
configure(obj, _root, index);
return obj;
@@ -419,7 +419,6 @@ public class XmlConfiguration
String className = node.getAttribute("class");
if (className == null)
return null;
-
return Loader.loadClass(className);
}
@@ -443,12 +442,12 @@ public class XmlConfiguration
XmlParser.Node node = (XmlParser.Node)o;
if ("Arg".equals(node.getTag()))
{
- LOG.warn("Ignored arg: "+node);
+ LOG.warn("Ignored arg: " + node);
continue;
}
break;
}
-
+
// Process real arguments
for (; i < cfg.size(); i++)
{
@@ -481,10 +480,10 @@ public class XmlConfiguration
newArray(obj, node);
break;
case "Map":
- newMap(obj,node);
+ newMap(obj, node);
break;
case "Ref":
- refObj(obj, node);
+ refObj(node);
break;
case "Property":
propertyObj(node);
@@ -501,26 +500,32 @@ public class XmlConfiguration
}
catch (Exception e)
{
- LOG.warn("Config error at " + node,e.toString()+" in "+_url);
+ LOG.warn("Config error at " + node, e.toString() + " in " + _url);
throw e;
}
}
}
- /*
- * Call a set method. This method makes a best effort to find a matching set method. The type of the value is used to find a suitable set method by 1.
- * Trying for a trivial type match. 2. Looking for a native type match. 3. Trying all correctly named methods for an auto conversion. 4. Attempting to
- * construct a suitable value from original value. @param obj
+ /**
+ *
Call a setter method.
+ *
This method makes a best effort to find a matching set method.
+ * The type of the value is used to find a suitable set method by:
+ *
+ *
Trying for a trivial type match
+ *
Looking for a native type match
+ *
Trying all correctly named methods for an auto conversion
+ *
Attempting to construct a suitable value from original value
+ *
*
- * @param node
+ * @param obj the enclosing object
+ * @param node the <Set> XML node
*/
private void set(Object obj, XmlParser.Node node) throws Exception
{
String attr = node.getAttribute("name");
- String name = "set" + attr.substring(0,1).toUpperCase(Locale.ENGLISH) + attr.substring(1);
- Object value = value(obj,node);
- Object[] arg =
- { value };
+ String name = "set" + attr.substring(0, 1).toUpperCase(Locale.ENGLISH) + attr.substring(1);
+ Object value = value(obj, node);
+ Object[] arg = {value};
Class> oClass = nodeClass(node);
if (oClass != null)
@@ -528,21 +533,20 @@ public class XmlConfiguration
else
oClass = obj.getClass();
- Class>[] vClass =
- { Object.class };
+ Class>[] vClass = {Object.class};
if (value != null)
vClass[0] = value.getClass();
if (LOG.isDebugEnabled())
- LOG.debug("XML " + (obj != null?obj.toString():oClass.getName()) + "." + name + "(" + value + ")");
+ LOG.debug("XML " + (obj != null ? obj.toString() : oClass.getName()) + "." + name + "(" + value + ")");
MultiException me = new MultiException();
-
+
// Try for trivial match
try
{
- Method set = oClass.getMethod(name,vClass);
- set.invoke(obj,arg);
+ Method set = oClass.getMethod(name, vClass);
+ set.invoke(obj, arg);
return;
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException e)
@@ -556,8 +560,8 @@ public class XmlConfiguration
{
Field type = vClass[0].getField("TYPE");
vClass[0] = (Class>)type.get(null);
- Method set = oClass.getMethod(name,vClass);
- set.invoke(obj,arg);
+ Method set = oClass.getMethod(name, vClass);
+ set.invoke(obj, arg);
return;
}
catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException e)
@@ -572,7 +576,7 @@ public class XmlConfiguration
Field field = oClass.getField(attr);
if (Modifier.isPublic(field.getModifiers()))
{
- field.set(obj,value);
+ field.set(obj, value);
return;
}
}
@@ -586,19 +590,19 @@ public class XmlConfiguration
Method[] sets = oClass.getMethods();
Method set = null;
String types = null;
- for (int s = 0; sets != null && s < sets.length; s++)
+ for (Method setter : sets)
{
- if (sets[s].getParameterCount()!=1)
+ if (setter.getParameterCount() != 1)
continue;
- Class>[] paramTypes = sets[s].getParameterTypes();
- if (name.equals(sets[s].getName()))
+ Class>[] paramTypes = setter.getParameterTypes();
+ if (name.equals(setter.getName()))
{
- types = types==null?paramTypes[0].getName():(types+","+paramTypes[0].getName());
+ types = types == null ? paramTypes[0].getName() : (types + "," + paramTypes[0].getName());
// lets try it
try
{
- set = sets[s];
- sets[s].invoke(obj,arg);
+ set = setter;
+ setter.invoke(obj, arg);
return;
}
catch (IllegalArgumentException | IllegalAccessException e)
@@ -610,11 +614,13 @@ public class XmlConfiguration
try
{
for (Class> c : __supportedCollections)
+ {
if (paramTypes[0].isAssignableFrom(c))
{
- sets[s].invoke(obj,convertArrayToCollection(value,c));
+ setter.invoke(obj, convertArrayToCollection(value, c));
return;
}
+ }
}
catch (IllegalAccessException e)
{
@@ -644,7 +650,7 @@ public class XmlConfiguration
Constructor> cons = sClass.getConstructor(vClass);
arg[0] = cons.newInstance(arg);
_configuration.initializeDefaults(arg[0]);
- set.invoke(obj,arg);
+ set.invoke(obj, arg);
return;
}
catch (NoSuchMethodException | IllegalAccessException | InstantiationException e)
@@ -656,15 +662,14 @@ public class XmlConfiguration
// No Joy
String message = oClass + "." + name + "(" + vClass[0] + ")";
- if (types!=null)
- message += ". Found setters for "+types;
- throw new NoSuchMethodException(message)
+ if (types != null)
+ message += ". Found setters for " + types;
+ NoSuchMethodException failure = new NoSuchMethodException(message);
+ for (int i = 0; i < me.size(); i++)
{
- {
- for (int i=0; i convertArrayToCollection(Object array, Class> collectionType)
{
+ if (array == null)
+ return null;
Collection> collection = null;
if (array.getClass().isArray())
{
@@ -682,7 +689,7 @@ public class XmlConfiguration
else if (collectionType.isAssignableFrom(HashSet.class))
collection = new HashSet<>(convertArrayToArrayList(array));
}
- if (collection==null)
+ if (collection == null)
throw new IllegalArgumentException("Can't convert \"" + array.getClass() + "\" to " + collectionType);
return collection;
}
@@ -692,14 +699,17 @@ public class XmlConfiguration
int length = Array.getLength(array);
ArrayList