Commit 92bb24e3 authored by Andy Wilkinson's avatar Andy Wilkinson

Avoid synchronizing on this and use an internal monitor instead

Where possible, code that previously synchronized on this (or on the
class in the case of static methods) has been updated to use an
internal monitor object instead. This allows the locking model that's
employed to be an implementation detail rather than part of the
class's API.

Classes that override a synchronized method continue to declare
the overriding method as synchronized. This ensures that locking
is consistent across the superclass and its subclass.

Closes gh-6262
parent f9c7db11
...@@ -33,6 +33,8 @@ public class InMemoryAuditEventRepository implements AuditEventRepository { ...@@ -33,6 +33,8 @@ public class InMemoryAuditEventRepository implements AuditEventRepository {
private static final int DEFAULT_CAPACITY = 4000; private static final int DEFAULT_CAPACITY = 4000;
private final Object monitor = new Object();
/** /**
* Circular buffer of the event with tail pointing to the last element. * Circular buffer of the event with tail pointing to the last element.
*/ */
...@@ -52,34 +54,40 @@ public class InMemoryAuditEventRepository implements AuditEventRepository { ...@@ -52,34 +54,40 @@ public class InMemoryAuditEventRepository implements AuditEventRepository {
* Set the capacity of this event repository. * Set the capacity of this event repository.
* @param capacity the capacity * @param capacity the capacity
*/ */
public synchronized void setCapacity(int capacity) { public void setCapacity(int capacity) {
this.events = new AuditEvent[capacity]; synchronized (this.monitor) {
this.events = new AuditEvent[capacity];
}
} }
@Override @Override
public synchronized void add(AuditEvent event) { public void add(AuditEvent event) {
Assert.notNull(event, "AuditEvent must not be null"); Assert.notNull(event, "AuditEvent must not be null");
this.tail = (this.tail + 1) % this.events.length; synchronized (this.monitor) {
this.events[this.tail] = event; this.tail = (this.tail + 1) % this.events.length;
this.events[this.tail] = event;
}
} }
@Override @Override
public synchronized List<AuditEvent> find(Date after) { public List<AuditEvent> find(Date after) {
return find(null, after, null); return find(null, after, null);
} }
@Override @Override
public synchronized List<AuditEvent> find(String principal, Date after) { public List<AuditEvent> find(String principal, Date after) {
return find(principal, after, null); return find(principal, after, null);
} }
@Override @Override
public synchronized List<AuditEvent> find(String principal, Date after, String type) { public List<AuditEvent> find(String principal, Date after, String type) {
LinkedList<AuditEvent> events = new LinkedList<AuditEvent>(); LinkedList<AuditEvent> events = new LinkedList<AuditEvent>();
for (int i = 0; i < this.events.length; i++) { synchronized (this.events) {
AuditEvent event = resolveTailEvent(i); for (int i = 0; i < this.events.length; i++) {
if (event != null && isMatch(principal, after, type, event)) { AuditEvent event = resolveTailEvent(i);
events.addFirst(event); if (event != null && isMatch(principal, after, type, event)) {
events.addFirst(event);
}
} }
} }
return events; return events;
......
...@@ -88,8 +88,8 @@ public class MailHealthIndicatorTests { ...@@ -88,8 +88,8 @@ public class MailHealthIndicatorTests {
} }
@Override @Override
public synchronized void connect(String host, int port, String user, public void connect(String host, int port, String user, String password)
String password) throws MessagingException { throws MessagingException {
} }
@Override @Override
......
...@@ -61,6 +61,8 @@ public class RunCommand extends OptionParsingCommand { ...@@ -61,6 +61,8 @@ public class RunCommand extends OptionParsingCommand {
private static class RunOptionHandler extends CompilerOptionHandler { private static class RunOptionHandler extends CompilerOptionHandler {
private final Object monitor = new Object();
private OptionSpec<Void> watchOption; private OptionSpec<Void> watchOption;
private OptionSpec<Void> verboseOption; private OptionSpec<Void> verboseOption;
...@@ -77,36 +79,39 @@ public class RunCommand extends OptionParsingCommand { ...@@ -77,36 +79,39 @@ public class RunCommand extends OptionParsingCommand {
this.quietOption = option(Arrays.asList("quiet", "q"), "Quiet logging"); this.quietOption = option(Arrays.asList("quiet", "q"), "Quiet logging");
} }
public synchronized void stop() { public void stop() {
if (this.runner != null) { synchronized (this.monitor) {
this.runner.stop(); if (this.runner != null) {
this.runner.stop();
}
this.runner = null;
} }
this.runner = null;
} }
@Override @Override
protected synchronized ExitStatus run(OptionSet options) throws Exception { protected synchronized ExitStatus run(OptionSet options) throws Exception {
synchronized (this.monitor) {
if (this.runner != null) {
throw new RuntimeException(
"Already running. Please stop the current application before running another (use the 'stop' command).");
}
if (this.runner != null) { SourceOptions sourceOptions = new SourceOptions(options);
throw new RuntimeException(
"Already running. Please stop the current application before running another (use the 'stop' command).");
}
SourceOptions sourceOptions = new SourceOptions(options);
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration(); .createDefaultRepositoryConfiguration();
repositoryConfiguration.add(0, new RepositoryConfiguration("local", repositoryConfiguration.add(0, new RepositoryConfiguration("local",
new File("repository").toURI(), true)); new File("repository").toURI(), true));
SpringApplicationRunnerConfiguration configuration = new SpringApplicationRunnerConfigurationAdapter( SpringApplicationRunnerConfiguration configuration = new SpringApplicationRunnerConfigurationAdapter(
options, this, repositoryConfiguration); options, this, repositoryConfiguration);
this.runner = new SpringApplicationRunner(configuration, this.runner = new SpringApplicationRunner(configuration,
sourceOptions.getSourcesArray(), sourceOptions.getArgsArray()); sourceOptions.getSourcesArray(), sourceOptions.getArgsArray());
this.runner.compileAndRun(); this.runner.compileAndRun();
return ExitStatus.OK; return ExitStatus.OK;
}
} }
/** /**
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -45,6 +45,8 @@ public class SpringApplicationRunner { ...@@ -45,6 +45,8 @@ public class SpringApplicationRunner {
private static int runnerCounter = 0; private static int runnerCounter = 0;
private final Object monitor = new Object();
private final SpringApplicationRunnerConfiguration configuration; private final SpringApplicationRunnerConfiguration configuration;
private final String[] sources; private final String[] sources;
...@@ -84,34 +86,38 @@ public class SpringApplicationRunner { ...@@ -84,34 +86,38 @@ public class SpringApplicationRunner {
} }
/** /**
* Compile and run the application. This method is synchronized as it can be called by * Compile and run the application.
* file monitoring threads. *
* @throws Exception on error * @throws Exception on error
*/ */
public synchronized void compileAndRun() throws Exception { public void compileAndRun() throws Exception {
try { synchronized (this.monitor) {
stop(); try {
Object[] compiledSources = compile(); stop();
monitorForChanges(); Object[] compiledSources = compile();
// Run in new thread to ensure that the context classloader is setup monitorForChanges();
this.runThread = new RunThread(compiledSources); // Run in new thread to ensure that the context classloader is setup
this.runThread.start(); this.runThread = new RunThread(compiledSources);
this.runThread.join(); this.runThread.start();
} this.runThread.join();
catch (Exception ex) {
if (this.fileWatchThread == null) {
throw ex;
} }
else { catch (Exception ex) {
ex.printStackTrace(); if (this.fileWatchThread == null) {
throw ex;
}
else {
ex.printStackTrace();
}
} }
} }
} }
public void stop() { public void stop() {
if (this.runThread != null) { synchronized (this.monitor) {
this.runThread.shutdown(); if (this.runThread != null) {
this.runThread = null; this.runThread.shutdown();
this.runThread = null;
}
} }
} }
...@@ -136,6 +142,8 @@ public class SpringApplicationRunner { ...@@ -136,6 +142,8 @@ public class SpringApplicationRunner {
*/ */
private class RunThread extends Thread { private class RunThread extends Thread {
private final Object monitor = new Object();
private final Object[] compiledSources; private final Object[] compiledSources;
private Object applicationContext; private Object applicationContext;
...@@ -155,33 +163,38 @@ public class SpringApplicationRunner { ...@@ -155,33 +163,38 @@ public class SpringApplicationRunner {
@Override @Override
public void run() { public void run() {
try { synchronized (this.monitor) {
this.applicationContext = new SpringApplicationLauncher( try {
getContextClassLoader()).launch(this.compiledSources, this.applicationContext = new SpringApplicationLauncher(
SpringApplicationRunner.this.args); getContextClassLoader()).launch(this.compiledSources,
} SpringApplicationRunner.this.args);
catch (Exception ex) { }
ex.printStackTrace(); catch (Exception ex) {
ex.printStackTrace();
}
} }
} }
/** /**
* Shutdown the thread, closing any previously opened application context. * Shutdown the thread, closing any previously opened application context.
*/ */
public synchronized void shutdown() { public void shutdown() {
if (this.applicationContext != null) { synchronized (this.monitor) {
try { if (this.applicationContext != null) {
Method method = this.applicationContext.getClass().getMethod("close"); try {
method.invoke(this.applicationContext); Method method = this.applicationContext.getClass()
} .getMethod("close");
catch (NoSuchMethodException ex) { method.invoke(this.applicationContext);
// Not an application context that we can close }
} catch (NoSuchMethodException ex) {
catch (Exception ex) { // Not an application context that we can close
ex.printStackTrace(); }
} catch (Exception ex) {
finally { ex.printStackTrace();
this.applicationContext = null; }
finally {
this.applicationContext = null;
}
} }
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -139,7 +139,7 @@ class Connection { ...@@ -139,7 +139,7 @@ class Connection {
} }
} }
private synchronized void writeWebSocketFrame(Frame frame) throws IOException { private void writeWebSocketFrame(Frame frame) throws IOException {
frame.write(this.outputStream); frame.write(this.outputStream);
} }
......
...@@ -53,6 +53,13 @@ public class LiveReloadServer { ...@@ -53,6 +53,13 @@ public class LiveReloadServer {
private static final int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(4); private static final int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(4);
private final ExecutorService executor = Executors
.newCachedThreadPool(new WorkerThreadFactory());
private final List<Connection> connections = new ArrayList<Connection>();
private final Object monitor = new Object();
private final int port; private final int port;
private final ThreadFactory threadFactory; private final ThreadFactory threadFactory;
...@@ -61,11 +68,6 @@ public class LiveReloadServer { ...@@ -61,11 +68,6 @@ public class LiveReloadServer {
private Thread listenThread; private Thread listenThread;
private ExecutorService executor = Executors
.newCachedThreadPool(new WorkerThreadFactory());
private List<Connection> connections = new ArrayList<Connection>();
/** /**
* Create a new {@link LiveReloadServer} listening on the default port. * Create a new {@link LiveReloadServer} listening on the default port.
*/ */
...@@ -112,29 +114,33 @@ public class LiveReloadServer { ...@@ -112,29 +114,33 @@ public class LiveReloadServer {
* Start the livereload server and accept incoming connections. * Start the livereload server and accept incoming connections.
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
*/ */
public synchronized void start() throws IOException { public void start() throws IOException {
Assert.state(!isStarted(), "Server already started"); synchronized (this.monitor) {
logger.debug("Starting live reload server on port " + this.port); Assert.state(!isStarted(), "Server already started");
this.serverSocket = new ServerSocket(this.port); logger.debug("Starting live reload server on port " + this.port);
this.listenThread = this.threadFactory.newThread(new Runnable() { this.serverSocket = new ServerSocket(this.port);
this.listenThread = this.threadFactory.newThread(new Runnable() {
@Override
public void run() { @Override
acceptConnections(); public void run() {
} acceptConnections();
}
}); });
this.listenThread.setDaemon(true); this.listenThread.setDaemon(true);
this.listenThread.setName("Live Reload Server"); this.listenThread.setName("Live Reload Server");
this.listenThread.start(); this.listenThread.start();
}
} }
/** /**
* Return if the server has been started. * Return if the server has been started.
* @return {@code true} if the server is running * @return {@code true} if the server is running
*/ */
public synchronized boolean isStarted() { public boolean isStarted() {
return this.listenThread != null; synchronized (this.monitor) {
return this.listenThread != null;
}
} }
/** /**
...@@ -168,30 +174,32 @@ public class LiveReloadServer { ...@@ -168,30 +174,32 @@ public class LiveReloadServer {
* Gracefully stop the livereload server. * Gracefully stop the livereload server.
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
*/ */
public synchronized void stop() throws IOException { public void stop() throws IOException {
if (this.listenThread != null) { synchronized (this.monitor) {
closeAllConnections(); if (this.listenThread != null) {
try { closeAllConnections();
this.executor.shutdown(); try {
this.executor.awaitTermination(1, TimeUnit.MINUTES); this.executor.shutdown();
} this.executor.awaitTermination(1, TimeUnit.MINUTES);
catch (InterruptedException ex) { }
Thread.currentThread().interrupt(); catch (InterruptedException ex) {
} Thread.currentThread().interrupt();
this.serverSocket.close(); }
try { this.serverSocket.close();
this.listenThread.join(); try {
} this.listenThread.join();
catch (InterruptedException ex) { }
Thread.currentThread().interrupt(); catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
this.listenThread = null;
this.serverSocket = null;
} }
this.listenThread = null;
this.serverSocket = null;
} }
} }
private void closeAllConnections() throws IOException { private void closeAllConnections() throws IOException {
synchronized (this.connections) { synchronized (this.monitor) {
for (Connection connection : this.connections) { for (Connection connection : this.connections) {
connection.close(); connection.close();
} }
...@@ -202,7 +210,7 @@ public class LiveReloadServer { ...@@ -202,7 +210,7 @@ public class LiveReloadServer {
* Trigger livereload of all connected clients. * Trigger livereload of all connected clients.
*/ */
public void triggerReload() { public void triggerReload() {
synchronized (this.connections) { synchronized (this.monitor) {
for (Connection connection : this.connections) { for (Connection connection : this.connections) {
try { try {
connection.triggerReload(); connection.triggerReload();
...@@ -215,13 +223,13 @@ public class LiveReloadServer { ...@@ -215,13 +223,13 @@ public class LiveReloadServer {
} }
private void addConnection(Connection connection) { private void addConnection(Connection connection) {
synchronized (this.connections) { synchronized (this.monitor) {
this.connections.add(connection); this.connections.add(connection);
} }
} }
private void removeConnection(Connection connection) { private void removeConnection(Connection connection) {
synchronized (this.connections) { synchronized (this.monitor) {
this.connections.remove(connection); this.connections.remove(connection);
} }
} }
......
...@@ -80,10 +80,24 @@ import org.springframework.util.ReflectionUtils; ...@@ -80,10 +80,24 @@ import org.springframework.util.ReflectionUtils;
*/ */
public class Restarter { public class Restarter {
private static final Object INSTANCE_MONITOR = new Object();
private static final String[] NO_ARGS = {}; private static final String[] NO_ARGS = {};
private static Restarter instance; private static Restarter instance;
private final Set<URL> urls = new LinkedHashSet<URL>();
private final ClassLoaderFiles classLoaderFiles = new ClassLoaderFiles();
private final Map<String, Object> attributes = new HashMap<String, Object>();
private final BlockingDeque<LeakSafeThread> leakSafeThreads = new LinkedBlockingDeque<LeakSafeThread>();
private final Lock stopLock = new ReentrantLock();
private final Object monitor = new Object();
private Log logger = new DeferredLog(); private Log logger = new DeferredLog();
private final boolean forceReferenceCleanup; private final boolean forceReferenceCleanup;
...@@ -100,18 +114,8 @@ public class Restarter { ...@@ -100,18 +114,8 @@ public class Restarter {
private final UncaughtExceptionHandler exceptionHandler; private final UncaughtExceptionHandler exceptionHandler;
private final Set<URL> urls = new LinkedHashSet<URL>();
private final ClassLoaderFiles classLoaderFiles = new ClassLoaderFiles();
private final Map<String, Object> attributes = new HashMap<String, Object>();
private final BlockingDeque<LeakSafeThread> leakSafeThreads = new LinkedBlockingDeque<LeakSafeThread>();
private boolean finished = false; private boolean finished = false;
private final Lock stopLock = new ReentrantLock();
private volatile ConfigurableApplicationContext rootContext; private volatile ConfigurableApplicationContext rootContext;
/** /**
...@@ -394,15 +398,20 @@ public class Restarter { ...@@ -394,15 +398,20 @@ public class Restarter {
* Called to finish {@link Restarter} initialization when application logging is * Called to finish {@link Restarter} initialization when application logging is
* available. * available.
*/ */
synchronized void finish() { void finish() {
if (!isFinished()) { synchronized (this.monitor) {
this.logger = DeferredLog.replay(this.logger, LogFactory.getLog(getClass())); if (!isFinished()) {
this.finished = true; this.logger = DeferredLog.replay(this.logger,
LogFactory.getLog(getClass()));
this.finished = true;
}
} }
} }
synchronized boolean isFinished() { boolean isFinished() {
return this.finished; synchronized (this.monitor) {
return this.finished;
}
} }
void prepare(ConfigurableApplicationContext applicationContext) { void prepare(ConfigurableApplicationContext applicationContext) {
...@@ -514,7 +523,7 @@ public class Restarter { ...@@ -514,7 +523,7 @@ public class Restarter {
public static void initialize(String[] args, boolean forceReferenceCleanup, public static void initialize(String[] args, boolean forceReferenceCleanup,
RestartInitializer initializer, boolean restartOnInitialize) { RestartInitializer initializer, boolean restartOnInitialize) {
Restarter localInstance = null; Restarter localInstance = null;
synchronized (Restarter.class) { synchronized (INSTANCE_MONITOR) {
if (instance == null) { if (instance == null) {
localInstance = new Restarter(Thread.currentThread(), args, localInstance = new Restarter(Thread.currentThread(), args,
forceReferenceCleanup, initializer); forceReferenceCleanup, initializer);
...@@ -531,9 +540,11 @@ public class Restarter { ...@@ -531,9 +540,11 @@ public class Restarter {
* {@link #initialize(String[]) initialization}. * {@link #initialize(String[]) initialization}.
* @return the restarter * @return the restarter
*/ */
public synchronized static Restarter getInstance() { public static Restarter getInstance() {
Assert.state(instance != null, "Restarter has not been initialized"); synchronized (INSTANCE_MONITOR) {
return instance; Assert.state(instance != null, "Restarter has not been initialized");
return instance;
}
} }
/** /**
...@@ -541,7 +552,9 @@ public class Restarter { ...@@ -541,7 +552,9 @@ public class Restarter {
* @param instance the instance to set * @param instance the instance to set
*/ */
final static void setInstance(Restarter instance) { final static void setInstance(Restarter instance) {
Restarter.instance = instance; synchronized (INSTANCE_MONITOR) {
Restarter.instance = instance;
}
} }
/** /**
...@@ -549,7 +562,9 @@ public class Restarter { ...@@ -549,7 +562,9 @@ public class Restarter {
* application code. * application code.
*/ */
public static void clearInstance() { public static void clearInstance() {
instance = null; synchronized (INSTANCE_MONITOR) {
instance = null;
}
} }
/** /**
......
...@@ -150,7 +150,7 @@ public class HttpTunnelConnection implements TunnelConnection { ...@@ -150,7 +150,7 @@ public class HttpTunnelConnection implements TunnelConnection {
return size; return size;
} }
private synchronized void openNewConnection(final HttpTunnelPayload payload) { private void openNewConnection(final HttpTunnelPayload payload) {
HttpTunnelConnection.this.executor.execute(new Runnable() { HttpTunnelConnection.this.executor.execute(new Runnable() {
@Override @Override
......
...@@ -46,12 +46,14 @@ public class TunnelClient implements SmartInitializingSingleton { ...@@ -46,12 +46,14 @@ public class TunnelClient implements SmartInitializingSingleton {
private static final Log logger = LogFactory.getLog(TunnelClient.class); private static final Log logger = LogFactory.getLog(TunnelClient.class);
private final TunnelClientListeners listeners = new TunnelClientListeners();
private final Object monitor = new Object();
private final int listenPort; private final int listenPort;
private final TunnelConnection tunnelConnection; private final TunnelConnection tunnelConnection;
private TunnelClientListeners listeners = new TunnelClientListeners();
private ServerThread serverThread; private ServerThread serverThread;
public TunnelClient(int listenPort, TunnelConnection tunnelConnection) { public TunnelClient(int listenPort, TunnelConnection tunnelConnection) {
...@@ -63,12 +65,14 @@ public class TunnelClient implements SmartInitializingSingleton { ...@@ -63,12 +65,14 @@ public class TunnelClient implements SmartInitializingSingleton {
@Override @Override
public void afterSingletonsInstantiated() { public void afterSingletonsInstantiated() {
if (this.serverThread == null) { synchronized (this.monitor) {
try { if (this.serverThread == null) {
start(); try {
} start();
catch (IOException ex) { }
throw new IllegalStateException(ex); catch (IOException ex) {
throw new IllegalStateException(ex);
}
} }
} }
} }
...@@ -77,35 +81,42 @@ public class TunnelClient implements SmartInitializingSingleton { ...@@ -77,35 +81,42 @@ public class TunnelClient implements SmartInitializingSingleton {
* Start the client and accept incoming connections on the port. * Start the client and accept incoming connections on the port.
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
*/ */
public synchronized void start() throws IOException { public void start() throws IOException {
Assert.state(this.serverThread == null, "Server already started"); synchronized (this.monitor) {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); Assert.state(this.serverThread == null, "Server already started");
serverSocketChannel.socket().bind(new InetSocketAddress(this.listenPort)); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
logger.trace("Listening for TCP traffic to tunnel on port " + this.listenPort); serverSocketChannel.socket().bind(new InetSocketAddress(this.listenPort));
this.serverThread = new ServerThread(serverSocketChannel); logger.trace(
this.serverThread.start(); "Listening for TCP traffic to tunnel on port " + this.listenPort);
this.serverThread = new ServerThread(serverSocketChannel);
this.serverThread.start();
}
} }
/** /**
* Stop the client, disconnecting any servers. * Stop the client, disconnecting any servers.
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
*/ */
public synchronized void stop() throws IOException { public void stop() throws IOException {
if (this.serverThread != null) { synchronized (this.monitor) {
logger.trace("Closing tunnel client on port " + this.listenPort); if (this.serverThread != null) {
this.serverThread.close(); logger.trace("Closing tunnel client on port " + this.listenPort);
try { this.serverThread.close();
this.serverThread.join(2000); try {
} this.serverThread.join(2000);
catch (InterruptedException ex) { }
// Ignore catch (InterruptedException ex) {
// Ignore
}
this.serverThread = null;
} }
this.serverThread = null;
} }
} }
protected final ServerThread getServerThread() { protected final ServerThread getServerThread() {
return this.serverThread; synchronized (this.monitor) {
return this.serverThread;
}
} }
public void addListener(TunnelClientListener listener) { public void addListener(TunnelClientListener listener) {
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
package org.springframework.boot.devtools.tunnel.client; package org.springframework.boot.devtools.tunnel.client;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -29,7 +29,7 @@ import org.springframework.util.Assert; ...@@ -29,7 +29,7 @@ import org.springframework.util.Assert;
*/ */
class TunnelClientListeners { class TunnelClientListeners {
private final List<TunnelClientListener> listeners = new ArrayList<TunnelClientListener>(); private final List<TunnelClientListener> listeners = new CopyOnWriteArrayList<TunnelClientListener>();
public void addListener(TunnelClientListener listener) { public void addListener(TunnelClientListener listener) {
Assert.notNull(listener, "Listener must not be null"); Assert.notNull(listener, "Listener must not be null");
......
...@@ -34,12 +34,14 @@ public class HttpTunnelPayloadForwarder { ...@@ -34,12 +34,14 @@ public class HttpTunnelPayloadForwarder {
private static final int MAXIMUM_QUEUE_SIZE = 100; private static final int MAXIMUM_QUEUE_SIZE = 100;
private final Map<Long, HttpTunnelPayload> queue = new HashMap<Long, HttpTunnelPayload>();
private final Object monitor = new Object();
private final WritableByteChannel targetChannel; private final WritableByteChannel targetChannel;
private long lastRequestSeq = 0; private long lastRequestSeq = 0;
private final Map<Long, HttpTunnelPayload> queue = new HashMap<Long, HttpTunnelPayload>();
/** /**
* Create a new {@link HttpTunnelPayloadForwarder} instance. * Create a new {@link HttpTunnelPayloadForwarder} instance.
* @param targetChannel the target channel * @param targetChannel the target channel
...@@ -49,20 +51,22 @@ public class HttpTunnelPayloadForwarder { ...@@ -49,20 +51,22 @@ public class HttpTunnelPayloadForwarder {
this.targetChannel = targetChannel; this.targetChannel = targetChannel;
} }
public synchronized void forward(HttpTunnelPayload payload) throws IOException { public void forward(HttpTunnelPayload payload) throws IOException {
long seq = payload.getSequence(); synchronized (this.monitor) {
if (this.lastRequestSeq != seq - 1) { long seq = payload.getSequence();
Assert.state(this.queue.size() < MAXIMUM_QUEUE_SIZE, if (this.lastRequestSeq != seq - 1) {
"Too many messages queued"); Assert.state(this.queue.size() < MAXIMUM_QUEUE_SIZE,
this.queue.put(seq, payload); "Too many messages queued");
return; this.queue.put(seq, payload);
} return;
payload.logOutgoing(); }
payload.writeTo(this.targetChannel); payload.logOutgoing();
this.lastRequestSeq = seq; payload.writeTo(this.targetChannel);
HttpTunnelPayload queuedItem = this.queue.get(seq + 1); this.lastRequestSeq = seq;
if (queuedItem != null) { HttpTunnelPayload queuedItem = this.queue.get(seq + 1);
forward(queuedItem); if (queuedItem != null) {
forward(queuedItem);
}
} }
} }
......
...@@ -28,14 +28,21 @@ public class Snake { ...@@ -28,14 +28,21 @@ public class Snake {
private static final int DEFAULT_LENGTH = 5; private static final int DEFAULT_LENGTH = 5;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final Object monitor = new Object();
private final int id; private final int id;
private final WebSocketSession session; private final WebSocketSession session;
private final String hexColor;
private Direction direction; private Direction direction;
private int length = DEFAULT_LENGTH; private int length = DEFAULT_LENGTH;
private Location head; private Location head;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final String hexColor;
public Snake(int id, WebSocketSession session) { public Snake(int id, WebSocketSession session) {
this.id = id; this.id = id;
...@@ -51,43 +58,49 @@ public class Snake { ...@@ -51,43 +58,49 @@ public class Snake {
this.length = DEFAULT_LENGTH; this.length = DEFAULT_LENGTH;
} }
private synchronized void kill() throws Exception { private void kill() throws Exception {
resetState(); synchronized (this.monitor) {
sendMessage("{'type': 'dead'}"); resetState();
sendMessage("{'type': 'dead'}");
}
} }
private synchronized void reward() throws Exception { private void reward() throws Exception {
this.length++; synchronized (this.monitor) {
sendMessage("{'type': 'kill'}"); this.length++;
sendMessage("{'type': 'kill'}");
}
} }
protected void sendMessage(String msg) throws Exception { protected void sendMessage(String msg) throws Exception {
this.session.sendMessage(new TextMessage(msg)); this.session.sendMessage(new TextMessage(msg));
} }
public synchronized void update(Collection<Snake> snakes) throws Exception { public void update(Collection<Snake> snakes) throws Exception {
Location nextLocation = this.head.getAdjacentLocation(this.direction); synchronized (this.monitor) {
if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) { Location nextLocation = this.head.getAdjacentLocation(this.direction);
nextLocation.x = 0; if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) {
} nextLocation.x = 0;
if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) { }
nextLocation.y = 0; if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) {
} nextLocation.y = 0;
if (nextLocation.x < 0) { }
nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH; if (nextLocation.x < 0) {
} nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH;
if (nextLocation.y < 0) { }
nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT; if (nextLocation.y < 0) {
} nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT;
if (this.direction != Direction.NONE) { }
this.tail.addFirst(this.head); if (this.direction != Direction.NONE) {
if (this.tail.size() > this.length) { this.tail.addFirst(this.head);
this.tail.removeLast(); if (this.tail.size() > this.length) {
this.tail.removeLast();
}
this.head = nextLocation;
} }
this.head = nextLocation;
}
handleCollisions(snakes); handleCollisions(snakes);
}
} }
private void handleCollisions(Collection<Snake> snakes) throws Exception { private void handleCollisions(Collection<Snake> snakes) throws Exception {
...@@ -104,29 +117,37 @@ public class Snake { ...@@ -104,29 +117,37 @@ public class Snake {
} }
} }
public synchronized Location getHead() { public Location getHead() {
return this.head; synchronized (this.monitor) {
return this.head;
}
} }
public synchronized Collection<Location> getTail() { public Collection<Location> getTail() {
return this.tail; synchronized (this.monitor) {
return this.tail;
}
} }
public synchronized void setDirection(Direction direction) { public void setDirection(Direction direction) {
this.direction = direction; synchronized (this.monitor) {
this.direction = direction;
}
} }
public synchronized String getLocationsJson() { public String getLocationsJson() {
StringBuilder sb = new StringBuilder(); synchronized (this.monitor) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x), StringBuilder sb = new StringBuilder();
Integer.valueOf(this.head.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x),
for (Location location : this.tail) { Integer.valueOf(this.head.y)));
sb.append(','); for (Location location : this.tail) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x), sb.append(',');
Integer.valueOf(location.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x),
Integer.valueOf(location.y)));
}
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
public int getId() { public int getId() {
......
...@@ -33,29 +33,35 @@ import org.slf4j.LoggerFactory; ...@@ -33,29 +33,35 @@ import org.slf4j.LoggerFactory;
*/ */
public class SnakeTimer { public class SnakeTimer {
private static final Logger log = LoggerFactory.getLogger(SnakeTimer.class); private static final long TICK_DELAY = 100;
private static Timer gameTimer = null; private static final Object MONITOR = new Object();
private static final long TICK_DELAY = 100; private static final Logger log = LoggerFactory.getLogger(SnakeTimer.class);
private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>(); private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
public static synchronized void addSnake(Snake snake) { private static Timer gameTimer = null;
if (snakes.isEmpty()) {
startTimer(); public static void addSnake(Snake snake) {
synchronized (MONITOR) {
if (snakes.isEmpty()) {
startTimer();
}
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
public static Collection<Snake> getSnakes() { public static Collection<Snake> getSnakes() {
return Collections.unmodifiableCollection(snakes.values()); return Collections.unmodifiableCollection(snakes.values());
} }
public static synchronized void removeSnake(Snake snake) { public static void removeSnake(Snake snake) {
snakes.remove(Integer.valueOf(snake.getId())); synchronized (MONITOR) {
if (snakes.isEmpty()) { snakes.remove(Integer.valueOf(snake.getId()));
stopTimer(); if (snakes.isEmpty()) {
stopTimer();
}
} }
} }
......
...@@ -28,14 +28,21 @@ public class Snake { ...@@ -28,14 +28,21 @@ public class Snake {
private static final int DEFAULT_LENGTH = 5; private static final int DEFAULT_LENGTH = 5;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final Object monitor = new Object();
private final int id; private final int id;
private final WebSocketSession session; private final WebSocketSession session;
private final String hexColor;
private Direction direction; private Direction direction;
private int length = DEFAULT_LENGTH; private int length = DEFAULT_LENGTH;
private Location head; private Location head;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final String hexColor;
public Snake(int id, WebSocketSession session) { public Snake(int id, WebSocketSession session) {
this.id = id; this.id = id;
...@@ -51,43 +58,49 @@ public class Snake { ...@@ -51,43 +58,49 @@ public class Snake {
this.length = DEFAULT_LENGTH; this.length = DEFAULT_LENGTH;
} }
private synchronized void kill() throws Exception { private void kill() throws Exception {
resetState(); synchronized (this.monitor) {
sendMessage("{'type': 'dead'}"); resetState();
sendMessage("{'type': 'dead'}");
}
} }
private synchronized void reward() throws Exception { private void reward() throws Exception {
this.length++; synchronized (this.monitor) {
sendMessage("{'type': 'kill'}"); this.length++;
sendMessage("{'type': 'kill'}");
}
} }
protected void sendMessage(String msg) throws Exception { protected void sendMessage(String msg) throws Exception {
this.session.sendMessage(new TextMessage(msg)); this.session.sendMessage(new TextMessage(msg));
} }
public synchronized void update(Collection<Snake> snakes) throws Exception { public void update(Collection<Snake> snakes) throws Exception {
Location nextLocation = this.head.getAdjacentLocation(this.direction); synchronized (this.monitor) {
if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) { Location nextLocation = this.head.getAdjacentLocation(this.direction);
nextLocation.x = 0; if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) {
} nextLocation.x = 0;
if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) { }
nextLocation.y = 0; if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) {
} nextLocation.y = 0;
if (nextLocation.x < 0) { }
nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH; if (nextLocation.x < 0) {
} nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH;
if (nextLocation.y < 0) { }
nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT; if (nextLocation.y < 0) {
} nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT;
if (this.direction != Direction.NONE) { }
this.tail.addFirst(this.head); if (this.direction != Direction.NONE) {
if (this.tail.size() > this.length) { this.tail.addFirst(this.head);
this.tail.removeLast(); if (this.tail.size() > this.length) {
this.tail.removeLast();
}
this.head = nextLocation;
} }
this.head = nextLocation;
}
handleCollisions(snakes); handleCollisions(snakes);
}
} }
private void handleCollisions(Collection<Snake> snakes) throws Exception { private void handleCollisions(Collection<Snake> snakes) throws Exception {
...@@ -104,29 +117,37 @@ public class Snake { ...@@ -104,29 +117,37 @@ public class Snake {
} }
} }
public synchronized Location getHead() { public Location getHead() {
return this.head; synchronized (this.monitor) {
return this.head;
}
} }
public synchronized Collection<Location> getTail() { public Collection<Location> getTail() {
return this.tail; synchronized (this.monitor) {
return this.tail;
}
} }
public synchronized void setDirection(Direction direction) { public void setDirection(Direction direction) {
this.direction = direction; synchronized (this.monitor) {
this.direction = direction;
}
} }
public synchronized String getLocationsJson() { public String getLocationsJson() {
StringBuilder sb = new StringBuilder(); synchronized (this.monitor) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x), StringBuilder sb = new StringBuilder();
Integer.valueOf(this.head.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x),
for (Location location : this.tail) { Integer.valueOf(this.head.y)));
sb.append(','); for (Location location : this.tail) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x), sb.append(',');
Integer.valueOf(location.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x),
Integer.valueOf(location.y)));
}
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
public int getId() { public int getId() {
......
...@@ -33,29 +33,35 @@ import org.apache.juli.logging.LogFactory; ...@@ -33,29 +33,35 @@ import org.apache.juli.logging.LogFactory;
*/ */
public class SnakeTimer { public class SnakeTimer {
private static final Log log = LogFactory.getLog(SnakeTimer.class); private static final long TICK_DELAY = 100;
private static Timer gameTimer = null; private static final Object MONITOR = new Object();
private static final long TICK_DELAY = 100; private static final Log log = LogFactory.getLog(SnakeTimer.class);
private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>(); private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
public static synchronized void addSnake(Snake snake) { private static Timer gameTimer = null;
if (snakes.isEmpty()) {
startTimer(); public static void addSnake(Snake snake) {
synchronized (MONITOR) {
if (snakes.isEmpty()) {
startTimer();
}
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
public static Collection<Snake> getSnakes() { public static Collection<Snake> getSnakes() {
return Collections.unmodifiableCollection(snakes.values()); return Collections.unmodifiableCollection(snakes.values());
} }
public static synchronized void removeSnake(Snake snake) { public static void removeSnake(Snake snake) {
snakes.remove(Integer.valueOf(snake.getId())); synchronized (MONITOR) {
if (snakes.isEmpty()) { snakes.remove(Integer.valueOf(snake.getId()));
stopTimer(); if (snakes.isEmpty()) {
stopTimer();
}
} }
} }
......
...@@ -28,14 +28,21 @@ public class Snake { ...@@ -28,14 +28,21 @@ public class Snake {
private static final int DEFAULT_LENGTH = 5; private static final int DEFAULT_LENGTH = 5;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final Object monitor = new Object();
private final int id; private final int id;
private final WebSocketSession session; private final WebSocketSession session;
private final String hexColor;
private Direction direction; private Direction direction;
private int length = DEFAULT_LENGTH; private int length = DEFAULT_LENGTH;
private Location head; private Location head;
private final Deque<Location> tail = new ArrayDeque<Location>();
private final String hexColor;
public Snake(int id, WebSocketSession session) { public Snake(int id, WebSocketSession session) {
this.id = id; this.id = id;
...@@ -51,43 +58,49 @@ public class Snake { ...@@ -51,43 +58,49 @@ public class Snake {
this.length = DEFAULT_LENGTH; this.length = DEFAULT_LENGTH;
} }
private synchronized void kill() throws Exception { private void kill() throws Exception {
resetState(); synchronized (this.monitor) {
sendMessage("{'type': 'dead'}"); resetState();
sendMessage("{'type': 'dead'}");
}
} }
private synchronized void reward() throws Exception { private void reward() throws Exception {
this.length++; synchronized (this.monitor) {
sendMessage("{'type': 'kill'}"); this.length++;
sendMessage("{'type': 'kill'}");
}
} }
protected void sendMessage(String msg) throws Exception { protected void sendMessage(String msg) throws Exception {
this.session.sendMessage(new TextMessage(msg)); this.session.sendMessage(new TextMessage(msg));
} }
public synchronized void update(Collection<Snake> snakes) throws Exception { public void update(Collection<Snake> snakes) throws Exception {
Location nextLocation = this.head.getAdjacentLocation(this.direction); synchronized (this.monitor) {
if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) { Location nextLocation = this.head.getAdjacentLocation(this.direction);
nextLocation.x = 0; if (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) {
} nextLocation.x = 0;
if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) { }
nextLocation.y = 0; if (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) {
} nextLocation.y = 0;
if (nextLocation.x < 0) { }
nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH; if (nextLocation.x < 0) {
} nextLocation.x = SnakeUtils.PLAYFIELD_WIDTH;
if (nextLocation.y < 0) { }
nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT; if (nextLocation.y < 0) {
} nextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT;
if (this.direction != Direction.NONE) { }
this.tail.addFirst(this.head); if (this.direction != Direction.NONE) {
if (this.tail.size() > this.length) { this.tail.addFirst(this.head);
this.tail.removeLast(); if (this.tail.size() > this.length) {
this.tail.removeLast();
}
this.head = nextLocation;
} }
this.head = nextLocation;
}
handleCollisions(snakes); handleCollisions(snakes);
}
} }
private void handleCollisions(Collection<Snake> snakes) throws Exception { private void handleCollisions(Collection<Snake> snakes) throws Exception {
...@@ -104,29 +117,37 @@ public class Snake { ...@@ -104,29 +117,37 @@ public class Snake {
} }
} }
public synchronized Location getHead() { public Location getHead() {
return this.head; synchronized (this.monitor) {
return this.head;
}
} }
public synchronized Collection<Location> getTail() { public Collection<Location> getTail() {
return this.tail; synchronized (this.monitor) {
return this.tail;
}
} }
public synchronized void setDirection(Direction direction) { public void setDirection(Direction direction) {
this.direction = direction; synchronized (this.monitor) {
this.direction = direction;
}
} }
public synchronized String getLocationsJson() { public String getLocationsJson() {
StringBuilder sb = new StringBuilder(); synchronized (this.monitor) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x), StringBuilder sb = new StringBuilder();
Integer.valueOf(this.head.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(this.head.x),
for (Location location : this.tail) { Integer.valueOf(this.head.y)));
sb.append(','); for (Location location : this.tail) {
sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x), sb.append(',');
Integer.valueOf(location.y))); sb.append(String.format("{x: %d, y: %d}", Integer.valueOf(location.x),
Integer.valueOf(location.y)));
}
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(this.id),
sb.toString());
} }
public int getId() { public int getId() {
......
...@@ -33,29 +33,35 @@ import org.slf4j.LoggerFactory; ...@@ -33,29 +33,35 @@ import org.slf4j.LoggerFactory;
*/ */
public class SnakeTimer { public class SnakeTimer {
private static final Logger log = LoggerFactory.getLogger(SnakeTimer.class); private static final long TICK_DELAY = 100;
private static Timer gameTimer = null; private static final Object MONITOR = new Object();
private static final long TICK_DELAY = 100; private static final Logger log = LoggerFactory.getLogger(SnakeTimer.class);
private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>(); private static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
public static synchronized void addSnake(Snake snake) { private static Timer gameTimer = null;
if (snakes.isEmpty()) {
startTimer(); public static void addSnake(Snake snake) {
synchronized (MONITOR) {
if (snakes.isEmpty()) {
startTimer();
}
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
snakes.put(Integer.valueOf(snake.getId()), snake);
} }
public static Collection<Snake> getSnakes() { public static Collection<Snake> getSnakes() {
return Collections.unmodifiableCollection(snakes.values()); return Collections.unmodifiableCollection(snakes.values());
} }
public static synchronized void removeSnake(Snake snake) { public static void removeSnake(Snake snake) {
snakes.remove(Integer.valueOf(snake.getId())); synchronized (MONITOR) {
if (snakes.isEmpty()) { snakes.remove(Integer.valueOf(snake.getId()));
stopTimer(); if (snakes.isEmpty()) {
stopTimer();
}
} }
} }
......
...@@ -89,6 +89,8 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor { ...@@ -89,6 +89,8 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
private static class Verification { private static class Verification {
private final Object monitor = new Object();
private final MockingProgress progress; private final MockingProgress progress;
Verification(Object target) { Verification(Object target) {
...@@ -101,25 +103,29 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor { ...@@ -101,25 +103,29 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
this.progress = (MockingProgress) ReflectionUtils.getField(field, container); this.progress = (MockingProgress) ReflectionUtils.getField(field, container);
} }
public synchronized boolean isVerifying() { public boolean isVerifying() {
VerificationMode mode = this.progress.pullVerificationMode(); synchronized (this.monitor) {
if (mode != null) { VerificationMode mode = this.progress.pullVerificationMode();
this.progress.verificationStarted(mode); if (mode != null) {
return true; this.progress.verificationStarted(mode);
return true;
}
return false;
} }
return false;
} }
public synchronized void replaceVerifyMock(Object source, Object target) { public void replaceVerifyMock(Object source, Object target) {
VerificationMode mode = this.progress.pullVerificationMode(); synchronized (this.monitor) {
if (mode != null) { VerificationMode mode = this.progress.pullVerificationMode();
if (mode instanceof MockAwareVerificationMode) { if (mode != null) {
MockAwareVerificationMode mockAwareMode = (MockAwareVerificationMode) mode; if (mode instanceof MockAwareVerificationMode) {
if (mockAwareMode.getMock() == source) { MockAwareVerificationMode mockAwareMode = (MockAwareVerificationMode) mode;
mode = new MockAwareVerificationMode(target, mockAwareMode); if (mockAwareMode.getMock() == source) {
mode = new MockAwareVerificationMode(target, mockAwareMode);
}
} }
this.progress.verificationStarted(mode);
} }
this.progress.verificationStarted(mode);
} }
} }
......
...@@ -426,6 +426,8 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo { ...@@ -426,6 +426,8 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
*/ */
class IsolatedThreadGroup extends ThreadGroup { class IsolatedThreadGroup extends ThreadGroup {
private final Object monitor = new Object();
private Throwable exception; private Throwable exception;
IsolatedThreadGroup(String name) { IsolatedThreadGroup(String name) {
...@@ -435,18 +437,21 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo { ...@@ -435,18 +437,21 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
@Override @Override
public void uncaughtException(Thread thread, Throwable ex) { public void uncaughtException(Thread thread, Throwable ex) {
if (!(ex instanceof ThreadDeath)) { if (!(ex instanceof ThreadDeath)) {
synchronized (this) { synchronized (this.monitor) {
this.exception = (this.exception == null ? ex : this.exception); this.exception = (this.exception == null ? ex : this.exception);
} }
getLog().warn(ex); getLog().warn(ex);
} }
} }
public synchronized void rethrowUncaughtException() public void rethrowUncaughtException() throws MojoExecutionException {
throws MojoExecutionException { synchronized (this.monitor) {
if (this.exception != null) { if (this.exception != null) {
throw new MojoExecutionException("An exception occurred while running. " throw new MojoExecutionException(
+ this.exception.getMessage(), this.exception); "An exception occurred while running. "
+ this.exception.getMessage(),
this.exception);
}
} }
} }
......
...@@ -51,6 +51,8 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer { ...@@ -51,6 +51,8 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(JettyEmbeddedServletContainer.class); .getLog(JettyEmbeddedServletContainer.class);
private final Object monitor = new Object();
private final Server server; private final Server server;
private final boolean autoStart; private final boolean autoStart;
...@@ -77,22 +79,24 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer { ...@@ -77,22 +79,24 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
initialize(); initialize();
} }
private synchronized void initialize() { private void initialize() {
try { synchronized (this.monitor) {
// Cache and clear the connectors to prevent requests being handled before try {
// the application context is ready // Cache and clear the connectors to prevent requests being handled before
this.connectors = this.server.getConnectors(); // the application context is ready
this.server.setConnectors(null); this.connectors = this.server.getConnectors();
this.server.setConnectors(null);
// Start the server so that the ServletContext is available
this.server.start(); // Start the server so that the ServletContext is available
this.server.setStopAtShutdown(false); this.server.start();
} this.server.setStopAtShutdown(false);
catch (Exception ex) { }
// Ensure process isn't left running catch (Exception ex) {
stopSilently(); // Ensure process isn't left running
throw new EmbeddedServletContainerException( stopSilently();
"Unable to start embedded Jetty servlet container", ex); throw new EmbeddedServletContainerException(
"Unable to start embedded Jetty servlet container", ex);
}
} }
} }
...@@ -191,16 +195,18 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer { ...@@ -191,16 +195,18 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
} }
@Override @Override
public synchronized void stop() { public void stop() {
try { synchronized (this.monitor) {
this.server.stop(); try {
} this.server.stop();
catch (InterruptedException ex) { }
Thread.currentThread().interrupt(); catch (InterruptedException ex) {
} Thread.currentThread().interrupt();
catch (Exception ex) { }
throw new EmbeddedServletContainerException( catch (Exception ex) {
"Unable to stop embedded Jetty servlet container", ex); throw new EmbeddedServletContainerException(
"Unable to stop embedded Jetty servlet container", ex);
}
} }
} }
......
...@@ -53,12 +53,14 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -53,12 +53,14 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(TomcatEmbeddedServletContainer.class); .getLog(TomcatEmbeddedServletContainer.class);
private static AtomicInteger containerCounter = new AtomicInteger(-1); private static final AtomicInteger containerCounter = new AtomicInteger(-1);
private final Tomcat tomcat; private final Object monitor = new Object();
private final Map<Service, Connector[]> serviceConnectors = new HashMap<Service, Connector[]>(); private final Map<Service, Connector[]> serviceConnectors = new HashMap<Service, Connector[]>();
private final Tomcat tomcat;
private final boolean autoStart; private final boolean autoStart;
/** /**
...@@ -81,37 +83,39 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -81,37 +83,39 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
initialize(); initialize();
} }
private synchronized void initialize() throws EmbeddedServletContainerException { private void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false)); .info("Tomcat initialized with port(s): " + getPortsDescription(false));
try { synchronized (this.monitor) {
addInstanceIdToEngineName(); try {
addInstanceIdToEngineName();
// Remove service connectors to that protocol binding doesn't happen yet // Remove service connectors to that protocol binding doesn't happen yet
removeServiceConnectors(); removeServiceConnectors();
// Start the server to trigger initialization listeners // Start the server to trigger initialization listeners
this.tomcat.start(); this.tomcat.start();
// We can re-throw failure exception directly in the main thread // We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions(); rethrowDeferredStartupExceptions();
Context context = findContext(); Context context = findContext();
try { try {
ContextBindings.bindClassLoader(context, getNamingToken(context), ContextBindings.bindClassLoader(context, getNamingToken(context),
getClass().getClassLoader()); getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
} }
catch (NamingException ex) { catch (Exception ex) {
// Naming is not enabled. Continue throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
} }
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
throw new EmbeddedServletContainerException("Unable to start embedded Tomcat",
ex);
} }
} }
...@@ -266,22 +270,24 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -266,22 +270,24 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
} }
@Override @Override
public synchronized void stop() throws EmbeddedServletContainerException { public void stop() throws EmbeddedServletContainerException {
try { synchronized (this.monitor) {
try { try {
stopTomcat(); try {
this.tomcat.destroy(); stopTomcat();
this.tomcat.destroy();
}
catch (LifecycleException ex) {
// swallow and continue
}
} }
catch (LifecycleException ex) { catch (Exception ex) {
// swallow and continue throw new EmbeddedServletContainerException(
"Unable to stop embedded Tomcat", ex);
}
finally {
containerCounter.decrementAndGet();
} }
}
catch (Exception ex) {
throw new EmbeddedServletContainerException("Unable to stop embedded Tomcat",
ex);
}
finally {
containerCounter.decrementAndGet();
} }
} }
......
...@@ -70,6 +70,8 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine ...@@ -70,6 +70,8 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(UndertowEmbeddedServletContainer.class); .getLog(UndertowEmbeddedServletContainer.class);
private final Object monitor = new Object();
private final Builder builder; private final Builder builder;
private final DeploymentManager manager; private final DeploymentManager manager;
...@@ -197,31 +199,33 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine ...@@ -197,31 +199,33 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
} }
@Override @Override
public synchronized void start() throws EmbeddedServletContainerException { public void start() throws EmbeddedServletContainerException {
try { synchronized (this.monitor) {
if (!this.autoStart) { try {
return; if (!this.autoStart) {
} return;
if (this.undertow == null) { }
this.undertow = createUndertowServer(); if (this.undertow == null) {
this.undertow = createUndertowServer();
}
this.undertow.start();
this.started = true;
UndertowEmbeddedServletContainer.logger
.info("Undertow started on port(s) " + getPortsDescription());
} }
this.undertow.start(); catch (Exception ex) {
this.started = true; if (findBindException(ex) != null) {
UndertowEmbeddedServletContainer.logger List<Port> failedPorts = getConfiguredPorts();
.info("Undertow started on port(s) " + getPortsDescription()); List<Port> actualPorts = getActualPorts();
} failedPorts.removeAll(actualPorts);
catch (Exception ex) { if (failedPorts.size() == 1) {
if (findBindException(ex) != null) { throw new PortInUseException(
List<Port> failedPorts = getConfiguredPorts(); failedPorts.iterator().next().getNumber());
List<Port> actualPorts = getActualPorts(); }
failedPorts.removeAll(actualPorts);
if (failedPorts.size() == 1) {
throw new PortInUseException(
failedPorts.iterator().next().getNumber());
} }
throw new EmbeddedServletContainerException(
"Unable to start embedded Undertow", ex);
} }
throw new EmbeddedServletContainerException(
"Unable to start embedded Undertow", ex);
} }
} }
...@@ -356,16 +360,18 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine ...@@ -356,16 +360,18 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
} }
@Override @Override
public synchronized void stop() throws EmbeddedServletContainerException { public void stop() throws EmbeddedServletContainerException {
if (this.started) { synchronized (this.monitor) {
try { if (this.started) {
this.started = false; try {
this.manager.stop(); this.started = false;
this.undertow.stop(); this.manager.stop();
} this.undertow.stop();
catch (Exception ex) { }
throw new EmbeddedServletContainerException("Unable to stop undertow", catch (Exception ex) {
ex); throw new EmbeddedServletContainerException("Unable to stop undertow",
ex);
}
} }
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -46,7 +46,7 @@ import org.springframework.util.StringUtils; ...@@ -46,7 +46,7 @@ import org.springframework.util.StringUtils;
public class PoolingConnectionFactoryBean extends PoolingConnectionFactory public class PoolingConnectionFactoryBean extends PoolingConnectionFactory
implements BeanNameAware, InitializingBean, DisposableBean { implements BeanNameAware, InitializingBean, DisposableBean {
private static ThreadLocal<PoolingConnectionFactoryBean> source = new ThreadLocal<PoolingConnectionFactoryBean>(); private static final ThreadLocal<PoolingConnectionFactoryBean> source = new ThreadLocal<PoolingConnectionFactoryBean>();
private String beanName; private String beanName;
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -50,7 +50,7 @@ import org.springframework.util.StringUtils; ...@@ -50,7 +50,7 @@ import org.springframework.util.StringUtils;
public class PoolingDataSourceBean extends PoolingDataSource public class PoolingDataSourceBean extends PoolingDataSource
implements BeanNameAware, InitializingBean { implements BeanNameAware, InitializingBean {
private static ThreadLocal<PoolingDataSourceBean> source = new ThreadLocal<PoolingDataSourceBean>(); private static final ThreadLocal<PoolingDataSourceBean> source = new ThreadLocal<PoolingDataSourceBean>();
private XADataSource dataSource; private XADataSource dataSource;
......
...@@ -51,7 +51,7 @@ public class AtomikosDataSourceBeanTests { ...@@ -51,7 +51,7 @@ public class AtomikosDataSourceBeanTests {
} }
@Override @Override
public synchronized void close() { public void close() {
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment