docs: public final void start() throws Exception {
  		synchronized (lock) {
  			Preconditions.checkState(state == State.CREATED, "The RestServerEndpoint cannot be restarted.");
  
  			log.info("Starting rest endpoint.");
  
  			final Router router = new Router();
  			final CompletableFuture<String> restAddressFuture = new CompletableFuture<>();
  
  			handlers = initializeHandlers(restAddressFuture);
  
  			/* sort the handlers such that they are ordered the following:
  			 * /jobs
  			 * /jobs/overview
  			 * /jobs/:jobid
  			 * /jobs/:jobid/config
  			 * /:*
  			 */
  			Collections.sort(
  				handlers,
  				RestHandlerUrlComparator.INSTANCE);
  
  			handlers.forEach(handler -> {
  				registerHandler(router, handler, log);
  			});
  
  			ChannelInitializer<SocketChannel> initializer = new ChannelInitializer<SocketChannel>() {
  
  				@Override
  				protected void initChannel(SocketChannel ch) {
  					RouterHandler handler = new RouterHandler(router, responseHeaders);
  
  					// SSL should be the first handler in the pipeline
  					if (isHttpsEnabled()) {
  						ch.pipeline().addLast("ssl",
  							new RedirectingSslHandler(restAddress, restAddressFuture, sslHandlerFactory));
  					}
  
  					ch.pipeline()
  						.addLast(new HttpServerCodec())
  						.addLast(new FileUploadHandler(uploadDir))
  						.addLast(new FlinkHttpObjectAggregator(maxContentLength, responseHeaders))
  						.addLast(new ChunkedWriteHandler())
  						.addLast(handler.getName(), handler)
  						.addLast(new PipelineErrorHandler(log, responseHeaders));
  				}
  			};
  
  			NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, new ExecutorThreadFactory("flink-rest-server-netty-boss"));
  			NioEventLoopGroup workerGroup = new NioEventLoopGroup(0, new ExecutorThreadFactory("flink-rest-server-netty-worker"));
  
  			bootstrap = new ServerBootstrap();
  			bootstrap
  				.group(bossGroup, workerGroup)
  				.channel(NioServerSocketChannel.class)
  				.childHandler(initializer);
  
  			Iterator<Integer> portsIterator;
  			try {
  				portsIterator = NetUtils.getPortRangeFromString(restBindPortRange);
  			} catch (IllegalConfigurationException e) {
  				throw e;
  			} catch (Exception e) {
  				throw new IllegalArgumentException("Invalid port range definition: " + restBindPortRange);
  			}
  
  			int chosenPort = 0;
  			while (portsIterator.hasNext()) {
  				try {
  					chosenPort = portsIterator.next();
  					final ChannelFuture channel;
  					if (restBindAddress == null) {
  						channel = bootstrap.bind(chosenPort);
  					} else {
  						channel = bootstrap.bind(restBindAddress, chosenPort);
  					}
  					serverChannel = channel.syncUninterruptibly().channel();
  					break;
  				} catch (final Exception e) {
  					// continue if the exception is due to the port being in use, fail early otherwise
  					if (!(e instanceof org.jboss.netty.channel.ChannelException || e instanceof java.net.BindException)) {
  						throw e;
  					}
  				}
  			}
  
  			if (serverChannel == null) {
  				throw new BindException("Could not start rest endpoint on any port in port range " + restBindPortRange);
  			}
  
  			log.debug("Binding rest endpoint to {}:{}.", restBindAddress, chosenPort);
  
  			final InetSocketAddress bindAddress = (InetSocketAddress) serverChannel.localAddress();
  			final String advertisedAddress;
  			if (bindAddress.getAddress().isAnyLocalAddress()) {
  				advertisedAddress = this.restAddress;
  			} else {
  				advertisedAddress = bindAddress.getAddress().getHostAddress();
  			}
  			final int port = bindAddress.getPort();
  
  			log.info("Rest endpoint listening at {}:{}", advertisedAddress, port);
  
  			restBaseUrl = new URL(determineProtocol(), advertisedAddress, port, "").toString();
  
  			restAddressFuture.complete(restBaseUrl);
  
  			state = State.RUNNING;
  
  			startInternal();
  		}
  	}