public synchronized CompletableFuture<Void> stop() {
        if (serverStopped != null) {
            // shutdown has started so don't fire it off again
            return serverStopped;
        }

        serverStopped = new CompletableFuture<>();
        final CountDownLatch servicesLeftToShutdown = new CountDownLatch(3);

        // it's possible that a channel might not be initialized in the first place if bind() fails because
        // of port conflict.  in that case, there's no need to wait for the channel to close.
        if (null == serverSocketChannel)
            servicesLeftToShutdown.countDown();
        else
            serverSocketChannel.close().addListener(f -> servicesLeftToShutdown.countDown());

        logger.info("Shutting down thread pools.");

        try {
            if (gremlinExecutorService != null) gremlinExecutorService.shutdown();
        } finally {
            logger.debug("Shutdown Gremlin thread pool.");
        }

        try {
            workerGroup.shutdownGracefully().addListener((GenericFutureListener) f -> servicesLeftToShutdown.countDown());
        } finally {
            logger.debug("Shutdown Worker thread pool.");
        }
        try {
            bossGroup.shutdownGracefully().addListener((GenericFutureListener) f -> servicesLeftToShutdown.countDown());
        } finally {
            logger.debug("Shutdown Boss thread pool.");
        }

        // channel is shutdown as are the thread pools - time to kill graphs as nothing else should be acting on them
        new Thread(() -> {
            if (serverGremlinExecutor != null) {
                serverGremlinExecutor.getHooks().forEach(hook -> {
                    logger.info("Executing shutdown {}", LifeCycleHook.class.getSimpleName());
                    try {
                        hook.onShutDown(new LifeCycleHook.Context(logger));
                    } catch (UnsupportedOperationException | UndeclaredThrowableException uoe) {
                        // if the user doesn't implement onShutDown the scriptengine will throw
                        // this exception.  it can safely be ignored.
                    }
                });
            }

            try {
                if (gremlinExecutorService != null) {
                    if (!gremlinExecutorService.awaitTermination(30000, TimeUnit.MILLISECONDS)) {
                        logger.warn("Gremlin thread pool did not fully terminate - continuing with shutdown process");
                    }
                }
            } catch (InterruptedException ie) {
                logger.warn("Timeout waiting for Gremlin thread pool to shutdown - continuing with shutdown process.");
            }

            try {
                servicesLeftToShutdown.await(30000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ie) {
                logger.warn("Timeout waiting for boss/worker thread pools to shutdown - continuing with shutdown process.");
            }

            // close TraversalSource and Graph instances - there aren't guarantees that closing Graph will close all
            // spawned TraversalSource instances so both should be closed directly and independently.
            if (serverGremlinExecutor != null) {
                final Set<String> traversalSourceNames = serverGremlinExecutor.getGraphManager().getTraversalSourceNames();
                traversalSourceNames.forEach(traversalSourceName -> {
                    logger.debug("Closing GraphTraversalSource instance [{}]", traversalSourceName);
                    try {
                        serverGremlinExecutor.getGraphManager().getTraversalSource(traversalSourceName).close();
                    } catch (Exception ex) {
                        logger.warn(String.format("Exception while closing GraphTraversalSource instance [%s]", traversalSourceName), ex);
                    } finally {
                        logger.info("Closed GraphTraversalSource instance [{}]", traversalSourceName);
                    }

                    try {
                        serverGremlinExecutor.getGraphManager().removeTraversalSource(traversalSourceName);
                    } catch (Exception ex) {
                        logger.warn(String.format("Exception while removing GraphTraversalSource instance [%s] from GraphManager", traversalSourceName), ex);
                    }
                });

                final Set<String> graphNames = serverGremlinExecutor.getGraphManager().getGraphNames();
                graphNames.forEach(gName -> {
                    logger.debug("Closing Graph instance [{}]", gName);
                    try {
                        final Graph graph = serverGremlinExecutor.getGraphManager().getGraph(gName);
                        graph.close();
                    } catch (Exception ex) {
                        logger.warn(String.format("Exception while closing Graph instance [%s]", gName), ex);
                    } finally {
                        logger.info("Closed Graph instance [{}]", gName);
                    }

                    try {
                        serverGremlinExecutor.getGraphManager().removeGraph(gName);
                    } catch (Exception ex) {
                        logger.warn(String.format("Exception while removing Graph instance [%s] from GraphManager", gName), ex);
                    }
                });
            }

            // kills reporter threads. this is a last bit of cleanup that can be done. typically, the jvm is headed
            // for shutdown which would obviously kill the reporters, but when it isn't they just keep reporting.
            // removing them all will silent them up and release the appropriate resources.
            MetricManager.INSTANCE.removeAllReporters();

            // removing all the metrics should allow Gremlin Server to clean up the metrics instance so that it can be
            // started again in the same JVM without those metrics initialized which generates a warning and won't
            // reset to start values
            MetricManager.INSTANCE.removeAllMetrics();

            logger.info("Gremlin Server - shutdown complete");
            serverStopped.complete(null);
        }, SERVER_THREAD_PREFIX + "stop").start();

        return serverStopped;
    }
Stop Gremlin Server and free the port binding. Note that multiple calls to this method will return the
same instance of the {@code CompletableFuture}.
gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/GremlinServer.java