/* * * * This file is part of the Hesperides distribution. * * (https://github.com/voyages-sncf-technologies/hesperides) * * Copyright (c) 2016 VSCT. * * * * Hesperides is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as * * published by the Free Software Foundation, version 3. * * * * Hesperides is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * * */ package com.vsct.dt.hesperides; import com.bazaarvoice.dropwizard.assets.ConfiguredAssetsBundle; import com.codahale.metrics.JmxReporter; import com.google.common.eventbus.EventBus; import com.sun.jersey.api.model.Parameter; import com.sun.jersey.spi.inject.InjectableProvider; import com.vsct.dt.hesperides.applications.Applications; import com.vsct.dt.hesperides.applications.ApplicationsAggregate; import com.vsct.dt.hesperides.applications.SnapshotRegistry; import com.vsct.dt.hesperides.applications.SnapshotRegistryInterface; import com.vsct.dt.hesperides.cache.HesperidesCacheResource; import com.vsct.dt.hesperides.events.EventsAggregate; import com.vsct.dt.hesperides.exception.wrapper.IllegalArgumentExceptionMapper; import com.vsct.dt.hesperides.exception.wrapper.*; import com.vsct.dt.hesperides.feedback.FeedbackConfiguration; import com.vsct.dt.hesperides.feedback.FeedbacksAggregate; import com.vsct.dt.hesperides.files.Files; import com.vsct.dt.hesperides.healthcheck.AggregateHealthCheck; import com.vsct.dt.hesperides.healthcheck.ElasticSearchHealthCheck; import com.vsct.dt.hesperides.healthcheck.EventStoreHealthCheck; import com.vsct.dt.hesperides.indexation.ElasticSearchClient; import com.vsct.dt.hesperides.indexation.ElasticSearchIndexationExecutor; import com.vsct.dt.hesperides.indexation.listeners.ModuleEventsIndexation; import com.vsct.dt.hesperides.indexation.listeners.PlatformEventsIndexation; import com.vsct.dt.hesperides.indexation.listeners.TemplateEventsIndexation; import com.vsct.dt.hesperides.indexation.search.ApplicationSearch; import com.vsct.dt.hesperides.indexation.search.ModuleSearch; import com.vsct.dt.hesperides.indexation.search.TemplateSearch; import com.vsct.dt.hesperides.resources.*; import com.vsct.dt.hesperides.security.BasicAuthProviderWithUserContextHolder; import com.vsct.dt.hesperides.security.CorrectedCachingAuthenticator; import com.vsct.dt.hesperides.security.DisabledAuthProvider; import com.vsct.dt.hesperides.security.ThreadLocalUserContext; import com.vsct.dt.hesperides.security.model.User; import com.vsct.dt.hesperides.storage.RedisEventStore; import com.vsct.dt.hesperides.templating.modules.ModulesAggregate; import com.vsct.dt.hesperides.templating.packages.TemplatePackagesAggregate; import com.vsct.dt.hesperides.templating.packages.virtual.CacheGeneratorApplicationAggregate; import com.vsct.dt.hesperides.templating.packages.virtual.CacheGeneratorModuleAggregate; import com.vsct.dt.hesperides.templating.packages.virtual.CacheGeneratorTemplatePackagesAggregate; import com.vsct.dt.hesperides.util.ManageableJedisConnection; import com.vsct.dt.hesperides.util.ManageableJedisConnectionInterface; import com.vsct.dt.hesperides.util.converter.ApplicationConverter; import com.vsct.dt.hesperides.util.converter.PropertiesConverter; import com.vsct.dt.hesperides.util.converter.TimeStampedPlatformConverter; import com.vsct.dt.hesperides.util.converter.impl.DefaultApplicationConverter; import com.vsct.dt.hesperides.util.converter.impl.DefaultPropertiesConverter; import com.vsct.dt.hesperides.util.converter.impl.DefaultTimeStampedPlatformConverter; import com.wordnik.swagger.config.ConfigFactory; import com.wordnik.swagger.config.ScannerFactory; import com.wordnik.swagger.config.SwaggerConfig; import com.wordnik.swagger.jaxrs.config.DefaultJaxrsScanner; import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider; import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON; import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider; import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader; import com.wordnik.swagger.reader.ClassReaders; import io.dropwizard.Application; import io.dropwizard.auth.Auth; import io.dropwizard.auth.Authenticator; import io.dropwizard.auth.basic.BasicCredentials; import io.dropwizard.client.HttpClientBuilder; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import org.apache.http.client.HttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Optional; public final class MainApplication extends Application<HesperidesConfiguration> { private static final Logger LOGGER = LoggerFactory.getLogger(MainApplication.class); public static void main(final String[] args) throws Exception { new MainApplication().run(args); } @Override public String getName() { return "hesperides"; } @Override public void initialize(final Bootstrap<HesperidesConfiguration> hesperidesConfigurationBootstrap) { hesperidesConfigurationBootstrap.addBundle(new ConfiguredAssetsBundle("/assets/", "/", "index.html")); } @Override public void run(final HesperidesConfiguration hesperidesConfiguration, final Environment environment) throws Exception { // ajoute swagger LOGGER.debug("Loading Swagger"); environment.jersey().register(new ApiListingResourceJSON()); environment.jersey().register(new ApiDeclarationProvider()); environment.jersey().register(new ResourceListingProvider()); ScannerFactory.setScanner(new DefaultJaxrsScanner()); ClassReaders.setReader(new DefaultJaxrsApiReader()); SwaggerConfig swaggerConfig = ConfigFactory.config(); swaggerConfig.setApiVersion(hesperidesConfiguration.getApiVersion()); swaggerConfig.setApiPath(""); swaggerConfig.setBasePath("/rest"); LOGGER.debug("Using Authentication Provider {}", hesperidesConfiguration.getAuthenticatorType()); Optional<Authenticator<BasicCredentials, User>> authenticator = hesperidesConfiguration.getAuthenticator(); InjectableProvider<Auth, Parameter> authProvider; ThreadLocalUserContext userContext = new ThreadLocalUserContext(environment.jersey()); if (authenticator.isPresent()) { authProvider = new BasicAuthProviderWithUserContextHolder( new CorrectedCachingAuthenticator<>( environment.metrics(), authenticator.get(), hesperidesConfiguration.getAuthenticationCachePolicy() ), "LOGIN AD POUR HESPERIDES", userContext, hesperidesConfiguration.useDefaultUserWhenAuthentFails()); } else { authProvider = new DisabledAuthProvider(); } environment.jersey().register(authProvider); LOGGER.debug("Creating Redis connection pool"); /* The Event Store, which is managed to properly close the connections */ // TODO best is abstract it in config file to allow MongoDB store or something else. final ManageableJedisConnectionInterface manageableJedisConnectionPool = new ManageableJedisConnection( hesperidesConfiguration.getRedisConfiguration()); final RedisEventStore eventStore; final ManageableJedisConnectionInterface snapshotManageableJedisConnectionPool = new ManageableJedisConnection( hesperidesConfiguration.getCacheConfiguration().getRedisConfiguration()); eventStore = new RedisEventStore(manageableJedisConnectionPool, snapshotManageableJedisConnectionPool); environment.lifecycle().manage(manageableJedisConnectionPool); LOGGER.debug("Creating Event Bus"); EventBus eventBus = new EventBus(); LOGGER.debug("Creating aggregates"); /* Create the http client */ HttpClient httpClient = new HttpClientBuilder(environment).using(hesperidesConfiguration.getHttpClientConfiguration()).build("elasticsearch"); environment.jersey().getResourceConfig().getRootResourceSingletons().add(httpClient); /* Create the elasticsearch client */ ElasticSearchClient elasticSearchClient = new ElasticSearchClient(httpClient, hesperidesConfiguration.getElasticSearchConfiguration()); /* Search Helpers */ ApplicationSearch applicationSearch = new ApplicationSearch(elasticSearchClient); ModuleSearch moduleSearch = new ModuleSearch(elasticSearchClient); TemplateSearch templateSearch = new TemplateSearch(elasticSearchClient); /* Registries (read part of the application) */ SnapshotRegistryInterface snapshotRegistryInterface = new SnapshotRegistry(manageableJedisConnectionPool.getPool()); /* Aggregates (write part of the application: events + method to read from registries) */ TemplatePackagesAggregate templatePackagesAggregate = new TemplatePackagesAggregate(eventBus, eventStore, userContext, hesperidesConfiguration); environment.lifecycle().manage(templatePackagesAggregate); ModulesAggregate modulesAggregate = new ModulesAggregate(eventBus, eventStore, templatePackagesAggregate, userContext, hesperidesConfiguration); environment.lifecycle().manage(modulesAggregate); ApplicationsAggregate applicationsAggregate = new ApplicationsAggregate(eventBus, eventStore, snapshotRegistryInterface, hesperidesConfiguration, userContext); environment.lifecycle().manage(applicationsAggregate); Applications permissionAwareApplications = new PermissionAwareApplicationsProxy(applicationsAggregate, userContext); /* Events aggregate */ EventsAggregate eventsAggregate = new EventsAggregate(hesperidesConfiguration.getEventsConfiguration(), eventStore); environment.lifecycle().manage(eventsAggregate); /* Feedbacks aggregate */ FeedbackConfiguration feedbackConfiguration = hesperidesConfiguration.getFeedbackConfiguration(); FeedbacksAggregate feedbacksAggregate; if (feedbackConfiguration == null) { feedbacksAggregate = null; } else { feedbacksAggregate = new FeedbacksAggregate(feedbackConfiguration, hesperidesConfiguration.getAssetsConfiguration(), hesperidesConfiguration.getProxyConfiguration()); environment.lifecycle().manage(feedbacksAggregate); } /* Service to generate files */ Files files = new Files(permissionAwareApplications, modulesAggregate, templatePackagesAggregate); LOGGER.debug("Loading indexation module"); /* The indexer */ ElasticSearchIndexationExecutor elasticSearchIndexationExecutor = new ElasticSearchIndexationExecutor(elasticSearchClient, hesperidesConfiguration.getElasticSearchConfiguration().getRetry(), hesperidesConfiguration.getElasticSearchConfiguration().getWaitBeforeRetryMs()); /* * Register indexation listeners */ eventBus.register(new ModuleEventsIndexation(elasticSearchIndexationExecutor)); eventBus.register(new PlatformEventsIndexation(elasticSearchIndexationExecutor)); eventBus.register(new TemplateEventsIndexation(elasticSearchIndexationExecutor)); LOGGER.debug("Creating web resources"); environment.jersey().setUrlPattern("/rest/*"); TimeStampedPlatformConverter timeStampedPlatformConverter = new DefaultTimeStampedPlatformConverter(); ApplicationConverter applicationConverter = new DefaultApplicationConverter(); PropertiesConverter propertiesConverter = new DefaultPropertiesConverter(); HesperidesTemplateResource templateResource = new HesperidesTemplateResource(templatePackagesAggregate, templateSearch); environment.jersey().register(templateResource); HesperidesModuleResource moduleResource = new HesperidesModuleResource(modulesAggregate, moduleSearch); environment.jersey().register(moduleResource); HesperidesApplicationResource applicationResource = new HesperidesApplicationResource( new PermissionAwareApplicationsProxy(applicationsAggregate, userContext), modulesAggregate, applicationSearch, timeStampedPlatformConverter, applicationConverter, propertiesConverter, moduleResource); environment.jersey().register(applicationResource); HesperidesStatsResource statsResource = new HesperidesStatsResource( new PermissionAwareApplicationsProxy(applicationsAggregate, userContext), modulesAggregate, templatePackagesAggregate); environment.jersey().register(statsResource); HesperidesFilesResource filesResource = new HesperidesFilesResource(files, moduleResource); environment.jersey().register(filesResource); HesperidesVersionsResource versionsResource = new HesperidesVersionsResource(hesperidesConfiguration.getBackendVersion(), hesperidesConfiguration.getApiVersion()); environment.jersey().register(versionsResource); HesperidesFullIndexationResource fullIndexationResource = new HesperidesFullIndexationResource(elasticSearchIndexationExecutor, applicationsAggregate, modulesAggregate, templatePackagesAggregate); environment.jersey().register(fullIndexationResource); // Events resource HesperidesEventResource eventResource = new HesperidesEventResource(eventsAggregate); environment.jersey().register(eventResource); final CacheGeneratorTemplatePackagesAggregate cacheTemplatePackagesAggregate = new CacheGeneratorTemplatePackagesAggregate(eventStore, hesperidesConfiguration); final CacheGeneratorModuleAggregate cacheModulesAggregate = new CacheGeneratorModuleAggregate(eventStore, hesperidesConfiguration); final CacheGeneratorApplicationAggregate cacheApplicationsAggregate = new CacheGeneratorApplicationAggregate(eventStore, hesperidesConfiguration); HesperidesCacheResource hesperidesCacheResource = new HesperidesCacheResource(templatePackagesAggregate, modulesAggregate, applicationsAggregate, cacheTemplatePackagesAggregate, cacheModulesAggregate, cacheApplicationsAggregate); environment.jersey().register(hesperidesCacheResource); // Users resource HesperidesUserResource userResource = new HesperidesUserResource(); environment.jersey().register(userResource); // Feedback resource HesperidesFeedbackRessource feedbackResource = new HesperidesFeedbackRessource(feedbacksAggregate); environment.jersey().register(feedbackResource); LOGGER.debug("Registering exception handlers"); /* Error handling */ environment.jersey().register(new DefaultExceptionMapper()); environment.jersey().register(new DuplicateResourceExceptionMapper()); environment.jersey().register(new IncoherentVersionExceptionMapper()); environment.jersey().register(new OutOfDateVersionExceptionMapper()); environment.jersey().register(new MissingResourceExceptionMapper()); environment.jersey().register(new IllegalArgumentExceptionMapper()); environment.jersey().register(new ForbiddenOperationExceptionMapper()); // ressource healthcheck environment.healthChecks().register("elasticsearch", new ElasticSearchHealthCheck(elasticSearchClient)); environment.healthChecks().register("aggregate_applications", new AggregateHealthCheck(applicationsAggregate)); environment.healthChecks().register("aggregate_modules", new AggregateHealthCheck(modulesAggregate)); environment.healthChecks().register("aggregate_template_packages", new AggregateHealthCheck(templatePackagesAggregate)); environment.healthChecks().register("event_store", new EventStoreHealthCheck(eventStore)); LOGGER.debug("Loading JMX Reporter"); /* Exposition JMX */ // active l'export des metrics en jmx JmxReporter reporter = JmxReporter.forRegistry(environment.metrics()).build(); reporter.start(); if (hesperidesConfiguration.getCacheConfiguration().isGenerateCaheOnStartup()) { LOGGER.info("Regenerate cache for template package."); cacheTemplatePackagesAggregate.regenerateCache(); LOGGER.info("Regenerate cache for module."); cacheModulesAggregate.regenerateCache(); LOGGER.info("Regenerate cache for application."); cacheApplicationsAggregate.regenerateCache(); LOGGER.info("All cache were regenerated successfully."); } if(hesperidesConfiguration.getElasticSearchConfiguration().reindexOnStartup()) { /* Reset the index */ fullIndexationResource.resetIndex(); } } }