content: package com.airbnb.airpal;
  
  import com.airbnb.airpal.core.AirpalUserFactory;
  import com.airbnb.airpal.core.health.PrestoHealthCheck;
  import com.airbnb.airpal.resources.ExecuteResource;
  import com.airbnb.airpal.resources.FilesResource;
  import com.airbnb.airpal.resources.HealthResource;
  import com.airbnb.airpal.resources.PingResource;
  import com.airbnb.airpal.resources.QueriesResource;
  import com.airbnb.airpal.resources.QueryResource;
  import com.airbnb.airpal.resources.ResultsPreviewResource;
  import com.airbnb.airpal.resources.S3FilesResource;
  import com.airbnb.airpal.resources.SessionResource;
  import com.airbnb.airpal.resources.TablesResource;
  import com.airbnb.airpal.resources.UserResource;
  import com.airbnb.airpal.resources.UsersResource;
  import com.airbnb.airpal.resources.sse.SSEEventSourceServlet;
  import com.google.inject.AbstractModule;
  import com.google.inject.Guice;
  import com.google.inject.Injector;
  import com.google.inject.Stage;
  import io.dropwizard.Application;
  import io.dropwizard.Bundle;
  import io.dropwizard.ConfiguredBundle;
  import io.dropwizard.assets.AssetsBundle;
  import io.dropwizard.db.DataSourceFactory;
  import io.dropwizard.flyway.FlywayBundle;
  import io.dropwizard.flyway.FlywayFactory;
  import io.dropwizard.jetty.BiDiGzipHandler;
  import io.dropwizard.setup.Bootstrap;
  import io.dropwizard.setup.Environment;
  import io.dropwizard.views.ViewBundle;
  import org.eclipse.jetty.server.Handler;
  
  import javax.servlet.ServletRegistration;
  
  import java.util.Arrays;
  
  import static org.glassfish.jersey.message.MessageProperties.IO_BUFFER_SIZE;
  
  public abstract class AirpalApplicationBase<T extends AirpalConfiguration>
          extends Application<T>
  {
      private static final String SERVER_SENT_EVENTS = "text/event-stream";
  
      protected Injector injector;
  
      @Override
      public void initialize(Bootstrap<T> bootstrap)
      {
          for (ConfiguredBundle<T> configuredBundle : getConfiguredBundles()) {
              bootstrap.addBundle(configuredBundle);
          }
          for (Bundle bundle : getBundles()) {
              bootstrap.addBundle(bundle);
          }
      }
  
      public abstract Iterable<AbstractModule> getModules(T config, Environment environment);
  
      public Iterable<ConfiguredBundle<T>> getConfiguredBundles()
      {
          return Arrays.asList(new ViewBundle());
      }
  
      public Iterable<Bundle> getBundles()
      {
          return Arrays.asList(
                  new AssetsBundle("/assets", "/app", "index.html"),
                  new FlywayBundle<T>()
                  {
                      @Override
                      public DataSourceFactory getDataSourceFactory(T configuration)
                      {
                          return configuration.getDataSourceFactory();
                      }
  
                      @Override
                      public FlywayFactory getFlywayFactory(T configuration)
                      {
                          return configuration.getFlywayFactory();
                      }
                  });
      }
  
      @Override
      public void run(T config, Environment environment)
              throws Exception
      {
          this.injector = Guice.createInjector(Stage.PRODUCTION, getModules(config, environment));
  
          System.setProperty(IO_BUFFER_SIZE, String.valueOf(config.getBufferSize().toBytes()));
  
          environment.healthChecks().register("presto", injector.getInstance(PrestoHealthCheck.class));
  
          environment.jersey().register(injector.getInstance(ExecuteResource.class));
          environment.jersey().register(injector.getInstance(QueryResource.class));
          environment.jersey().register(injector.getInstance(QueriesResource.class));
          environment.jersey().register(injector.getInstance(UserResource.class));
          environment.jersey().register(injector.getInstance(UsersResource.class));
          environment.jersey().register(injector.getInstance(TablesResource.class));
          environment.jersey().register(injector.getInstance(HealthResource.class));
          environment.jersey().register(injector.getInstance(PingResource.class));
          environment.jersey().register(injector.getInstance(SessionResource.class));
          environment.jersey().register(injector.getInstance(FilesResource.class));
          environment.jersey().register(injector.getInstance(ResultsPreviewResource.class));
          environment.jersey().register(injector.getInstance(S3FilesResource.class));
  
          environment.jersey().register(injector.getInstance(AirpalUserFactory.class));
  
          // Setup SSE (Server Sent Events)
          ServletRegistration.Dynamic sseServlet = environment.servlets()
                  .addServlet("updates", injector.getInstance(SSEEventSourceServlet.class));
          sseServlet.setAsyncSupported(true);
          sseServlet.addMapping("/api/updates/subscribe");
  
          // Disable GZIP content encoding for SSE endpoints
          environment.lifecycle().addServerLifecycleListener(server -> {
              for (Handler handler : server.getChildHandlersByClass(BiDiGzipHandler.class)) {
                  ((BiDiGzipHandler) handler).addExcludedMimeTypes(SERVER_SENT_EVENTS);
              }
          });
      }
  }
  