code: @Override
    public synchronized void start() {
      if (this.hasStarted) {
        LOG.warn("ApplicationLauncher has already started");
        return;
      }
      this.hasStarted = true;
  
      this.serviceManager = new ServiceManager(this.services);
      // A listener that shutdowns the application if any service fails.
      this.serviceManager.addListener(new ServiceManager.Listener() {
        @Override
        public void failure(Service service) {
          super.failure(service);
          LOG.error(String.format("Service %s has failed.", service.getClass().getSimpleName()), service.failureCause());
          try {
            service.stopAsync();
            ServiceBasedAppLauncher.this.stop();
          } catch (ApplicationException ae) {
            LOG.error("Could not shutdown services gracefully. This may cause the application to hang.");
          }
        }
      });
  
      Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
          try {
            ServiceBasedAppLauncher.this.stop();
          } catch (ApplicationException e) {
            LOG.error("Failed to shutdown application", e);
          } finally {
            try {
              ServiceBasedAppLauncher.this.close();
            } catch (IOException e) {
              LOG.error("Failed to close application", e);
            }
          }
        }
      });
  
      LOG.info("Starting the Gobblin application and all its associated Services");
  
      // Start the application
      this.serviceManager.startAsync().awaitHealthy();
    }
docs: Starts the {@link ApplicationLauncher} by starting all associated services. This method also adds a shutdown hook
  that invokes {@link #stop()} and the {@link #close()} methods. So {@link #stop()} and {@link #close()} need not be
  called explicitly; they can be triggered during the JVM shutdown.