code: @Override
  	public void close() {
  		boolean enqueueOnSocketContextClose;
  		synchronized(closeLock) {
  			if(!closed) {
  				closed = true;
  				enqueueOnSocketContextClose = true;
  			} else {
  				enqueueOnSocketContextClose = false;
  			}
  		}
  		List<S> socketsToClose;
  		synchronized(sockets) {
  			// Gets a copy of the sockets to avoid concurrent modification exception and avoid holding lock
  			socketsToClose = new ArrayList<>(sockets.values());
  			// Each will be removed from socket.close() below: sockets.clear();
  		}
  		for(S socket : socketsToClose) {
  			try {
  				socket.close();
  			} catch(Exception exc) {
  				logger.log(Level.SEVERE, null, exc);
  			}
  		}
  		if(enqueueOnSocketContextClose) {
  			Future<?> future = listenerManager.enqueueEvent(
  				new ConcurrentListenerManager.Event<SocketContextListener>() {
  					@Override
  					public Runnable createCall(final SocketContextListener listener) {
  						return new Runnable() {
  							@Override
  							public void run() {
  								listener.onSocketContextClose(AbstractSocketContext.this);
  							}
  						};
  					}
  				}
  			);
  			try {
  				future.get();
  			} catch(ExecutionException e) {
  				logger.log(Level.SEVERE, null, e);
  			} catch(InterruptedException e) {
  				logger.log(Level.SEVERE, null, e);
  				// Restore the interrupted status
  				Thread.currentThread().interrupt();
  			}
  		}
  		listenerManager.close();
  		synchronized(sockets) {
  			if(!sockets.isEmpty()) throw new AssertionError("Not all sockets closed");
  		}
  	}
docs: Any overriding implementation must call super.close() first.