mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
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
This commit is contained in:
parent
f9c7db1137
commit
92bb24e365
@ -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<AuditEvent> find(Date after) {
|
||||
public List<AuditEvent> find(Date after) {
|
||||
return find(null, after, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<AuditEvent> find(String principal, Date after) {
|
||||
public List<AuditEvent> find(String principal, Date after) {
|
||||
return find(principal, after, null);
|
||||
}
|
||||
|
||||
@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>();
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -61,6 +61,8 @@ public class RunCommand extends OptionParsingCommand {
|
||||
|
||||
private static class RunOptionHandler extends CompilerOptionHandler {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private OptionSpec<Void> watchOption;
|
||||
|
||||
private OptionSpec<Void> 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> 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> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<Connection> connections = new ArrayList<Connection>();
|
||||
|
||||
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<Connection> connections = new ArrayList<Connection>();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<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 final boolean forceReferenceCleanup;
|
||||
@ -100,18 +114,8 @@ public class Restarter {
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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<TunnelClientListener> listeners = new ArrayList<TunnelClientListener>();
|
||||
private final List<TunnelClientListener> listeners = new CopyOnWriteArrayList<TunnelClientListener>();
|
||||
|
||||
public void addListener(TunnelClientListener listener) {
|
||||
Assert.notNull(listener, "Listener must not be null");
|
||||
|
@ -34,12 +34,14 @@ public class HttpTunnelPayloadForwarder {
|
||||
|
||||
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 long lastRequestSeq = 0;
|
||||
|
||||
private final Map<Long, HttpTunnelPayload> queue = new HashMap<Long, HttpTunnelPayload>();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,15 +28,22 @@ public class Snake {
|
||||
|
||||
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 WebSocketSession session;
|
||||
|
||||
private Direction direction;
|
||||
private int length = DEFAULT_LENGTH;
|
||||
private Location head;
|
||||
private final Deque<Location> tail = new ArrayDeque<Location>();
|
||||
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<Snake> 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<Snake> 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<Snake> snakes) throws Exception {
|
||||
@ -104,29 +117,37 @@ public class Snake {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Location getHead() {
|
||||
return this.head;
|
||||
}
|
||||
|
||||
public synchronized Collection<Location> 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<Location> 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() {
|
||||
|
@ -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<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
|
||||
|
||||
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<Snake> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,15 +28,22 @@ public class Snake {
|
||||
|
||||
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 WebSocketSession session;
|
||||
|
||||
private Direction direction;
|
||||
private int length = DEFAULT_LENGTH;
|
||||
private Location head;
|
||||
private final Deque<Location> tail = new ArrayDeque<Location>();
|
||||
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<Snake> 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<Snake> 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<Snake> snakes) throws Exception {
|
||||
@ -104,29 +117,37 @@ public class Snake {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Location getHead() {
|
||||
return this.head;
|
||||
}
|
||||
|
||||
public synchronized Collection<Location> 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<Location> 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() {
|
||||
|
@ -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<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
|
||||
|
||||
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<Snake> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,15 +28,22 @@ public class Snake {
|
||||
|
||||
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 WebSocketSession session;
|
||||
|
||||
private Direction direction;
|
||||
private int length = DEFAULT_LENGTH;
|
||||
private Location head;
|
||||
private final Deque<Location> tail = new ArrayDeque<Location>();
|
||||
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<Snake> 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<Snake> 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<Snake> snakes) throws Exception {
|
||||
@ -104,29 +117,37 @@ public class Snake {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Location getHead() {
|
||||
return this.head;
|
||||
}
|
||||
|
||||
public synchronized Collection<Location> 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<Location> 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() {
|
||||
|
@ -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<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();
|
||||
|
||||
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<Snake> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Service, Connector[]> serviceConnectors = new HashMap<Service, Connector[]>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Port> failedPorts = getConfiguredPorts();
|
||||
List<Port> 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<Port> failedPorts = getConfiguredPorts();
|
||||
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
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PoolingConnectionFactoryBean> source = new ThreadLocal<PoolingConnectionFactoryBean>();
|
||||
private static final ThreadLocal<PoolingConnectionFactoryBean> source = new ThreadLocal<PoolingConnectionFactoryBean>();
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
@ -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<PoolingDataSourceBean> source = new ThreadLocal<PoolingDataSourceBean>();
|
||||
private static final ThreadLocal<PoolingDataSourceBean> source = new ThreadLocal<PoolingDataSourceBean>();
|
||||
|
||||
private XADataSource dataSource;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class AtomikosDataSourceBeanTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user