io.trino.gateway.baseapp.BaseApp Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.gateway.baseapp;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.MoreCollectors;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import io.airlift.log.Logger;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthFilter;
import io.dropwizard.core.Application;
import io.dropwizard.core.Configuration;
import io.dropwizard.core.server.DefaultServerFactory;
import io.dropwizard.core.setup.Environment;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.lifecycle.setup.LifecycleEnvironment;
import io.trino.gateway.ha.config.HaGatewayConfiguration;
import io.trino.gateway.ha.log.GatewayRequestLogFactory;
import io.trino.gateway.ha.module.RouterBaseModule;
import io.trino.gateway.ha.module.StochasticRoutingManagerProvider;
import io.trino.gateway.ha.resource.EntityEditorResource;
import io.trino.gateway.ha.resource.GatewayResource;
import io.trino.gateway.ha.resource.GatewayViewResource;
import io.trino.gateway.ha.resource.GatewayWebAppResource;
import io.trino.gateway.ha.resource.HaGatewayResource;
import io.trino.gateway.ha.resource.LoginResource;
import io.trino.gateway.ha.resource.PublicResource;
import io.trino.gateway.ha.resource.TrinoResource;
import io.trino.gateway.ha.security.AuthorizedExceptionMapper;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static java.lang.String.format;
/**
* Supports Guice in Dropwizard.
*
* To use it, create a subclass and provide a list of modules you want to use with the {@link
* #addModules} method.
*
*
Packages supplied in the constructor will be scanned for Resources, Providers, and Managed
* classes, and added to the environment.
*
*
GuiceApplication also makes {@link com.codahale.metrics.MetricRegistry} available for
* injection.
*/
public abstract class BaseApp
extends Application
{
private static final Logger logger = Logger.get(BaseApp.class);
private final ImmutableList.Builder appModules = ImmutableList.builder();
private Module newModule(String clazz, HaGatewayConfiguration configuration, Environment environment)
{
try {
logger.info("Trying to load module [%s]", clazz);
// Modules must have exactly one constructor. The signature must be either one:
// public Module constructor(HaGatewayConfiguration)
// public Module constructor(HaGatewayConfiguration, Environment)
Constructor>[] constructors = Class.forName(clazz).getConstructors();
if (constructors.length != 1) {
throw new RuntimeException(format("Failed to load module [%s]. Multiple constructors exist.", clazz));
}
Constructor> constructor = constructors[0];
Object module = switch (constructor.getParameterCount()) {
case 1 -> constructor.newInstance(configuration);
case 2 -> constructor.newInstance(configuration, environment);
default -> throw new RuntimeException(format("Failed to load module [%s]. Unsupported constructor.", clazz));
};
return ((Module) module);
}
catch (Exception e) {
logger.error(e, "Could not instantiate module [%s]", clazz);
onFatalError(e);
}
return null;
}
private void validateModules(List modules, HaGatewayConfiguration configuration, Environment environment)
{
Optional routerProvider = modules.stream()
.filter(module -> module instanceof RouterBaseModule)
.collect(MoreCollectors.toOptional());
if (routerProvider.isEmpty()) {
logger.warn("Router provider doesn't exist in the config, using the StochasticRoutingManagerProvider");
String clazz = StochasticRoutingManagerProvider.class.getCanonicalName();
modules.add(newModule(clazz, configuration, environment));
}
}
@Override // Using Airlift logger
protected void bootstrapLogging() {}
/**
* When the application runs, this is called after the bundles are run.
*
* @param configuration the parsed {@link Configuration} object
* @param environment the application's {@link Environment}
* @throws Exception if something goes wrong
*/
@Override
public void run(HaGatewayConfiguration configuration, Environment environment)
throws Exception
{
((DefaultServerFactory) configuration.getServerFactory()).setRequestLogFactory(new GatewayRequestLogFactory());
configureGuice(configuration, environment);
}
private void configureGuice(HaGatewayConfiguration configuration, Environment environment)
{
appModules.add(new MetricRegistryModule(environment.metrics()));
appModules.addAll(addModules(configuration, environment));
Injector injector = Guice.createInjector(appModules.build());
injector.injectMembers(this);
registerWithInjector(configuration, environment, injector);
}
private void registerWithInjector(HaGatewayConfiguration configuration, Environment environment, Injector injector)
{
logger.info("op=register_start configuration=%s", configuration.toString());
registerAuthFilters(environment, injector);
registerProviders(environment, injector);
addManagedApps(configuration, environment, injector);
registerResources(environment, injector);
logger.info("op=register_end configuration=%s", configuration.toString());
}
/**
* Supply a list of modules to be used by Guice.
*
* @param configuration the app configuration
* @return a list of modules to be provisioned by Guice
*/
protected List addModules(HaGatewayConfiguration configuration, Environment environment)
{
List modules = new ArrayList<>();
if (configuration.getModules() == null) {
logger.warn("No modules to load.");
return modules;
}
for (String clazz : configuration.getModules()) {
modules.add(newModule(clazz, configuration, environment));
}
validateModules(modules, configuration, environment);
return modules;
}
/**
* Supply a list of managed apps.
*/
protected List addManagedApps(
HaGatewayConfiguration configuration, Environment environment, Injector injector)
{
List managedApps = new ArrayList<>();
if (configuration.getManagedApps() == null) {
logger.error("No managed apps found");
return managedApps;
}
configuration
.getManagedApps()
.forEach(
clazz -> {
try {
Class c = Class.forName(clazz);
LifecycleEnvironment lifecycle = environment.lifecycle();
lifecycle.manage((Managed) injector.getInstance(c));
logger.info("op=register type=managed item=%s", c);
}
catch (Exception e) {
logger.error(e, "Error loading managed app");
}
});
return managedApps;
}
private void registerProviders(Environment environment, Injector injector)
{
final Set> classes = ImmutableSet.of(AuthorizedExceptionMapper.class);
classes.forEach(
c -> {
environment.jersey().register(injector.getInstance(c));
logger.info("op=register type=provider item=%s", c);
});
}
private void registerResources(Environment environment, Injector injector)
{
final Set> classes = ImmutableSet.of(
EntityEditorResource.class,
GatewayResource.class,
GatewayViewResource.class,
GatewayWebAppResource.class,
HaGatewayResource.class,
LoginResource.class,
PublicResource.class,
TrinoResource.class);
classes.forEach(
c -> {
environment.jersey().register(injector.getInstance(c));
logger.info("op=register type=resource item=%s", c);
});
}
private void registerAuthFilters(Environment environment, Injector injector)
{
environment
.jersey()
.register(new AuthDynamicFeature(injector.getInstance(AuthFilter.class)));
logger.info("op=register type=auth filter item=%s", AuthFilter.class);
environment.jersey().register(RolesAllowedDynamicFeature.class);
}
}