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