package com.linkedin.thirdeye.dashboard;
import com.linkedin.thirdeye.client.ThirdEyeCacheRegistry;
import com.linkedin.thirdeye.common.BaseThirdEyeApplication;
import com.linkedin.thirdeye.dashboard.resources.AdminResource;
import com.linkedin.thirdeye.dashboard.resources.AnomalyFunctionResource;
import com.linkedin.thirdeye.dashboard.resources.AnomalyResource;
import com.linkedin.thirdeye.dashboard.resources.CacheResource;
import com.linkedin.thirdeye.dashboard.resources.DashboardResource;
import com.linkedin.thirdeye.dashboard.resources.DataCompletenessResource;
import com.linkedin.thirdeye.dashboard.resources.DatasetConfigResource;
import com.linkedin.thirdeye.dashboard.resources.EmailResource;
import com.linkedin.thirdeye.dashboard.resources.EntityManagerResource;
import com.linkedin.thirdeye.dashboard.resources.EntityMappingResource;
import com.linkedin.thirdeye.dashboard.resources.IngraphDashboardConfigResource;
import com.linkedin.thirdeye.dashboard.resources.IngraphMetricConfigResource;
import com.linkedin.thirdeye.dashboard.resources.JobResource;
import com.linkedin.thirdeye.dashboard.resources.MetricConfigResource;
import com.linkedin.thirdeye.dashboard.resources.OnboardResource;
import com.linkedin.thirdeye.dashboard.resources.OverrideConfigResource;
import com.linkedin.thirdeye.dashboard.resources.SummaryResource;
import com.linkedin.thirdeye.dashboard.resources.ThirdEyeResource;
import com.linkedin.thirdeye.dashboard.resources.v2.AnomaliesResource;
import com.linkedin.thirdeye.dashboard.resources.v2.DataResource;
import com.linkedin.thirdeye.dashboard.resources.v2.EventResource;
import com.linkedin.thirdeye.dashboard.resources.v2.RootCauseEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.RootCauseResource;
import com.linkedin.thirdeye.dashboard.resources.v2.TimeSeriesResource;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.DefaultEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.FormatterLoader;
import com.linkedin.thirdeye.detector.email.filter.AlertFilterFactory;
import com.linkedin.thirdeye.detector.function.AnomalyFunctionFactory;
import com.linkedin.thirdeye.rootcause.Pipeline;
import com.linkedin.thirdeye.rootcause.RCAFramework;
import com.linkedin.thirdeye.rootcause.impl.PipelineLoader;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.views.ViewBundle;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import java.util.EnumSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ThirdEyeDashboardApplication
extends BaseThirdEyeApplication<ThirdEyeDashboardConfiguration> {
private static final Logger LOG = LoggerFactory.getLogger(ThirdEyeDashboardApplication.class);
@Override
public String getName() {
return "Thirdeye Dashboard";
}
@Override
public void initialize(Bootstrap<ThirdEyeDashboardConfiguration> bootstrap) {
bootstrap.addBundle(new ViewBundle());
bootstrap.addBundle(new HelperBundle());
bootstrap.addBundle(new AssetsBundle("/app/", "/app", "index.html", "app"));
bootstrap.addBundle(new AssetsBundle("/assets", "/assets", null, "assets"));
bootstrap.addBundle(new AssetsBundle("/assets/css", "/assets/css", null, "css"));
bootstrap.addBundle(new AssetsBundle("/assets/js", "/assets/js", null, "js"));
bootstrap.addBundle(new AssetsBundle("/assets/lib", "/assets/lib", null, "lib"));
bootstrap.addBundle(new AssetsBundle("/assets/img", "/assets/img", null, "img"));
bootstrap.addBundle(new AssetsBundle("/assets/data", "/assets/data", null, "data"));
}
@Override
public void run(ThirdEyeDashboardConfiguration config, Environment env)
throws Exception {
LOG.info("isCors value {}", config.isCors());
if (config.isCors()) {
FilterRegistration.Dynamic corsFilter = env.servlets().addFilter("CORS", CrossOriginFilter.class);
corsFilter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
corsFilter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
corsFilter.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
corsFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
}
super.initDAOs();
try {
ThirdEyeCacheRegistry.initializeCaches(config);
} catch (Exception e) {
LOG.error("Exception while loading caches", e);
}
AnomalyFunctionFactory anomalyFunctionFactory = new AnomalyFunctionFactory(config.getFunctionConfigPath());
AlertFilterFactory alertFilterFactory = new AlertFilterFactory(config.getAlertFilterConfigPath());
env.jersey().register(new AnomalyFunctionResource(config.getFunctionConfigPath()));
env.jersey().register(new DashboardResource());
env.jersey().register(new CacheResource());
env.jersey().register(new AnomalyResource(anomalyFunctionFactory, alertFilterFactory));
env.jersey().register(new EmailResource(config));
env.jersey().register(new EntityManagerResource());
env.jersey().register(new IngraphMetricConfigResource());
env.jersey().register(new MetricConfigResource());
env.jersey().register(new DatasetConfigResource());
env.jersey().register(new IngraphDashboardConfigResource());
env.jersey().register(new JobResource());
env.jersey().register(new AdminResource());
env.jersey().register(new SummaryResource());
env.jersey().register(new ThirdEyeResource());
env.jersey().register(new OverrideConfigResource());
env.jersey().register(new DataResource(anomalyFunctionFactory, alertFilterFactory));
env.jersey().register(new AnomaliesResource(anomalyFunctionFactory, alertFilterFactory));
env.jersey().register(new TimeSeriesResource());
env.jersey().register(new OnboardResource());
env.jersey().register(new EventResource(config));
env.jersey().register(new DataCompletenessResource(DAO_REGISTRY.getDataCompletenessConfigDAO()));
env.jersey().register(new EntityMappingResource());
env.jersey().register(new RootCauseResource(makeRCAFramework(config), makeRCAFormatters(config)));
}
private static RCAFramework makeRCAFramework(ThirdEyeDashboardConfiguration config) throws Exception {
if(config.getRcaConfigPath() == null)
throw new IllegalArgumentException("rcaConfigPath must not be null");
File configFile = new File(config.getRcaConfigPath());
if(!configFile.isAbsolute())
configFile = new File(config.getRootDir() + File.separator + configFile);
List<Pipeline> pipelines = PipelineLoader.getPipelinesFromConfig(configFile);
return new RCAFramework(pipelines, Executors.newFixedThreadPool(config.getRcaParallelism()));
}
private static List<RootCauseEntityFormatter> makeRCAFormatters(ThirdEyeDashboardConfiguration config) throws Exception {
List<RootCauseEntityFormatter> formatters = new ArrayList<>();
if(config.getRcaFormatters() != null) {
for(String className : config.getRcaFormatters()) {
try {
formatters.add(FormatterLoader.fromClassName(className));
} catch(ClassNotFoundException e) {
LOG.warn("Could not find formatter class '{}'. Skipping.", className, e);
}
}
}
formatters.add(new DefaultEntityFormatter());
return formatters;
}
public static void main(String[] args) throws Exception {
if (args.length == 0) {
throw new IllegalArgumentException("Please provide config directory as parameter");
}
String thirdEyeConfigDir = args[0];
System.setProperty("dw.rootDir", thirdEyeConfigDir);
String dashboardApplicationConfigFile = thirdEyeConfigDir + "/" + "dashboard.yml";
new ThirdEyeDashboardApplication().run("server", dashboardApplicationConfigFile);
}
}