All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.swisspush.gateleen.playground.Server Maven / Gradle / Ivy

Go to download

Middleware library based on Vert.x to build advanced JSON/REST communication servers

There is a newer version: 2.1.15
Show newest version
package org.swisspush.gateleen.playground;

import io.vertx.core.*;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.tracing.TracingPolicy;
import io.vertx.ext.web.RoutingContext;
import io.vertx.redis.client.PoolOptions;
import io.vertx.redis.client.RedisAPI;
import io.vertx.redis.client.RedisStandaloneConnectOptions;
import io.vertx.redis.client.impl.RedisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.swisspush.gateleen.cache.CacheHandler;
import org.swisspush.gateleen.cache.fetch.CacheDataFetcher;
import org.swisspush.gateleen.cache.fetch.DefaultCacheDataFetcher;
import org.swisspush.gateleen.cache.storage.CacheStorage;
import org.swisspush.gateleen.cache.storage.RedisCacheStorage;
import org.swisspush.gateleen.core.configuration.ConfigurationResourceManager;
import org.swisspush.gateleen.core.cors.CORSHandler;
import org.swisspush.gateleen.core.event.EventBusHandler;
import org.swisspush.gateleen.core.exception.GateleenExceptionFactory;
import org.swisspush.gateleen.core.http.ClientRequestCreator;
import org.swisspush.gateleen.core.http.LocalHttpClient;
import org.swisspush.gateleen.core.lock.Lock;
import org.swisspush.gateleen.core.lock.impl.RedisBasedLock;
import org.swisspush.gateleen.core.redis.RedisProvider;
import org.swisspush.gateleen.core.resource.CopyResourceHandler;
import org.swisspush.gateleen.core.storage.EventBusResourceStorage;
import org.swisspush.gateleen.core.storage.ResourceStorage;
import org.swisspush.gateleen.core.util.Address;
import org.swisspush.gateleen.delegate.DelegateHandler;
import org.swisspush.gateleen.delta.DeltaHandler;
import org.swisspush.gateleen.expansion.ExpansionHandler;
import org.swisspush.gateleen.expansion.ZipExtractHandler;
import org.swisspush.gateleen.hook.HookHandler;
import org.swisspush.gateleen.hook.reducedpropagation.ReducedPropagationManager;
import org.swisspush.gateleen.hook.reducedpropagation.impl.RedisReducedPropagationStorage;
import org.swisspush.gateleen.kafka.KafkaHandler;
import org.swisspush.gateleen.kafka.KafkaMessageSender;
import org.swisspush.gateleen.kafka.KafkaMessageValidator;
import org.swisspush.gateleen.kafka.KafkaProducerRepository;
import org.swisspush.gateleen.logging.DefaultLogAppenderRepository;
import org.swisspush.gateleen.logging.LogAppenderRepository;
import org.swisspush.gateleen.logging.LogController;
import org.swisspush.gateleen.logging.LoggingResourceManager;
import org.swisspush.gateleen.monitoring.CustomRedisMonitor;
import org.swisspush.gateleen.monitoring.MonitoringHandler;
import org.swisspush.gateleen.monitoring.ResetMetricsController;
import org.swisspush.gateleen.qos.QoSHandler;
import org.swisspush.gateleen.queue.queuing.QueueClient;
import org.swisspush.gateleen.queue.queuing.QueueProcessor;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.QueueCircuitBreaker;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.QueueCircuitBreakerStorage;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.api.QueueCircuitBreakerHttpRequestHandler;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.configuration.QueueCircuitBreakerConfigurationResourceManager;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.impl.QueueCircuitBreakerImpl;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.impl.RedisQueueCircuitBreakerStorage;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.util.QueueCircuitBreakerRulePatternToCircuitMapping;
import org.swisspush.gateleen.queue.queuing.splitter.QueueSplitter;
import org.swisspush.gateleen.queue.queuing.splitter.QueueSplitterImpl;
import org.swisspush.gateleen.routing.CustomHttpResponseHandler;
import org.swisspush.gateleen.routing.DeferCloseHttpClient;
import org.swisspush.gateleen.routing.Router;
import org.swisspush.gateleen.routing.RuleProvider;
import org.swisspush.gateleen.routing.auth.DefaultOAuthProvider;
import org.swisspush.gateleen.runconfig.RunConfig;
import org.swisspush.gateleen.scheduler.SchedulerResourceManager;
import org.swisspush.gateleen.security.PatternHolder;
import org.swisspush.gateleen.security.authorization.Authorizer;
import org.swisspush.gateleen.security.content.ContentTypeConstraintHandler;
import org.swisspush.gateleen.security.content.ContentTypeConstraintRepository;
import org.swisspush.gateleen.user.RoleProfileHandler;
import org.swisspush.gateleen.user.UserProfileHandler;
import org.swisspush.gateleen.validation.*;

import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Map;

import static org.swisspush.gateleen.core.exception.GateleenExceptionFactory.newGateleenWastefulExceptionFactory;

/**
 * Playground server to try Gateleen at home.
 *
 * @author https://github.com/lbovet [Laurent Bovet]
 */
public class Server extends AbstractVerticle {

    private Logger log = LoggerFactory.getLogger(Server.class);

    private static final String PREFIX = RunConfig.SERVER_NAME + ".";
    private static final String ROOT = "/playground";
    private static final String SERVER_ROOT = ROOT + "/server";
    private static final String RULES_ROOT = SERVER_ROOT + "/admin/v1/routing/rules";
    private static final String RETURN_HTTP_STATUS_ROOT = SERVER_ROOT + "/return-with-status-code";
    private static final String ROLE_PATTERN = "^z-playground[-_](.*)$";
    private static final String ROLE_PREFIX  = "z-playground-";

    private static final String JMX_DOMAIN = "org.swisspush.gateleen";

    /*
     * Ports
     */
    private int defaultRedisPort = 6379;
    private int mainPort = 7012;
    private int circuitBreakerPort = 7013;
    private int storagePort = 8989;

    private HttpServer mainServer;
    private RedisClient redisClient;
    private RedisAPI redisApi;
    private ResourceStorage storage;
    private CacheStorage cacheStorage;
    private CacheDataFetcher cacheDataFetcher;
    private final GateleenExceptionFactory exceptionFactory = newGateleenWastefulExceptionFactory();
    private Authorizer authorizer;
    private Router router;

    /*
     * Managers
     */
    private LoggingResourceManager loggingResourceManager;
    private LogAppenderRepository logAppenderRepository;
    private ConfigurationResourceManager configurationResourceManager;
    private ValidationResourceManager validationResourceManager;
    private ValidationSchemaProvider validationSchemaProvider;
    private Validator validator;
    private SchedulerResourceManager schedulerResourceManager;
    private QueueCircuitBreakerConfigurationResourceManager queueCircuitBreakerConfigurationResourceManager;
    private ReducedPropagationManager reducedPropagationManager;

    /*
     * Handlers
     */
    private MonitoringHandler monitoringHandler;
    private EventBusHandler eventBusHandler;
    private UserProfileHandler userProfileHandler;
    private RoleProfileHandler roleProfileHandler;
    private CORSHandler corsHandler;
    private ExpansionHandler expansionHandler;
    private DeltaHandler deltaHandler;
    private CopyResourceHandler copyResourceHandler;
    private ValidationHandler validationHandler;
    private QoSHandler qosHandler;
    private HookHandler hookHandler;
    private ZipExtractHandler zipExtractHandler;
    private DelegateHandler delegateHandler;
    private KafkaHandler kafkaHandler;
    private CustomHttpResponseHandler customHttpResponseHandler;
    private ContentTypeConstraintHandler contentTypeConstraintHandler;
    private CacheHandler cacheHandler;

    private QueueSplitter queueSplitter;

    public static void main(String[] args) {
        Vertx.vertx().deployVerticle("org.swisspush.gateleen.playground.Server", event ->
                LoggerFactory.getLogger(Server.class).info("[_] Gateleen - http://localhost:7012/gateleen/")
        );
    }

    @Override
    public void start() {
        final LocalHttpClient selfClient = new LocalHttpClient(vertx, exceptionFactory);
        final JsonObject info = new JsonObject();
        final Map props = RunConfig.buildRedisProps("localhost", defaultRedisPort);

        /*
         * Just for demonstration purposes. In real-life use a request header name to group the requests
         * and set it as vm option like -Dorg.swisspush.request.rule.property=MY_HEADER_NAME
         */
        System.setProperty(MonitoringHandler.REQUEST_PER_RULE_PROPERTY, "x-appid");

        try {
            String externalConfig = System.getProperty("org.swisspush.config.dir") + "/config.properties";
            Resource externalConfigResource = new FileSystemResource(externalConfig);
            if (externalConfigResource.exists()) {
                log.info("Merging external config {}", externalConfig);
                props.putAll(RunConfig.subMap(new ResourcePropertySource(externalConfigResource).getSource(), "redis."));
            } else {
                log.info("No external config found under {}", externalConfig);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String redisHost = (String) props.get("redis.host");
        Integer redisPort = (Integer) props.get("redis.port");
        boolean redisEnableTls = props.get("redis.enableTls") != null ? (Boolean) props.get("redis.enableTls") : false;

        props.put(ExpansionHandler.MAX_EXPANSION_LEVEL_HARD_PROPERTY, "100");
        props.put(ExpansionHandler.MAX_EXPANSION_LEVEL_SOFT_PROPERTY, "50");

        RunConfig.deployModules(vertx, Server.class, props, success -> {
            if (success) {
                String protocol = redisEnableTls ? "rediss://" : "redis://";
                redisClient = new RedisClient(vertx, new NetClientOptions(), new PoolOptions(), new RedisStandaloneConnectOptions().setConnectionString(protocol + redisHost + ":" + redisPort), TracingPolicy.IGNORE);
                redisApi = RedisAPI.api(redisClient);
                RedisProvider redisProvider = () -> Future.succeededFuture(redisApi);

                new CustomRedisMonitor(vertx, redisProvider, "main", "rest-storage", 10).start();
                storage = new EventBusResourceStorage(vertx.eventBus(), Address.storageAddress() + "-main", exceptionFactory);
                corsHandler = new CORSHandler();

                RuleProvider ruleProvider = new RuleProvider(vertx, RULES_ROOT, storage, props);

                deltaHandler = new DeltaHandler(vertx, redisProvider, selfClient, ruleProvider, loggingResourceManager,
                        logAppenderRepository, true);
                expansionHandler = new ExpansionHandler(ruleProvider, selfClient, props, ROOT);
                copyResourceHandler = new CopyResourceHandler(selfClient, exceptionFactory, SERVER_ROOT + "/v1/copy");
                monitoringHandler = new MonitoringHandler(vertx, storage, PREFIX, SERVER_ROOT + "/monitoring/rpr");

                Lock lock = new RedisBasedLock(redisProvider, newGateleenWastefulExceptionFactory());

                cacheStorage = new RedisCacheStorage(vertx, lock, redisProvider, exceptionFactory, 20 * 1000);
                cacheDataFetcher = new DefaultCacheDataFetcher(new ClientRequestCreator(selfClient));
                cacheHandler = new CacheHandler(cacheDataFetcher, cacheStorage, SERVER_ROOT + "/cache");

                qosHandler = new QoSHandler(vertx, storage, SERVER_ROOT + "/admin/v1/qos", props, PREFIX);
                qosHandler.enableResourceLogging(true);

                configurationResourceManager = new ConfigurationResourceManager(vertx, storage, exceptionFactory);
                configurationResourceManager.enableResourceLogging(true);

                eventBusHandler = new EventBusHandler(vertx, SERVER_ROOT + "/event/v1/",
                        SERVER_ROOT + "/event/v1/sock/", "event/channels/",
                        "channels/([^/]+).*", configurationResourceManager, SERVER_ROOT + "/admin/v1/hookconfig");
                eventBusHandler.setEventbusBridgePingInterval(RunConfig.EVENTBUS_BRIDGE_PING_INTERVAL);

                logAppenderRepository = new DefaultLogAppenderRepository(vertx);
                loggingResourceManager = new LoggingResourceManager(vertx, storage, SERVER_ROOT + "/admin/v1/logging");
                loggingResourceManager.enableResourceLogging(true);


                ContentTypeConstraintRepository repository = new ContentTypeConstraintRepository();
                contentTypeConstraintHandler = new ContentTypeConstraintHandler(configurationResourceManager, repository,
                        SERVER_ROOT + "/admin/v1/contentTypeConstraints",
                        Arrays.asList(
                                new PatternHolder("application/json"),
                                new PatternHolder("application/x-www-form-urlencoded"),
                                new PatternHolder("multipart/form-data")
                        ));
                contentTypeConstraintHandler.initialize();

                userProfileHandler = new UserProfileHandler(vertx, storage, RunConfig.buildUserProfileConfiguration());
                userProfileHandler.enableResourceLogging(true);

                roleProfileHandler = new RoleProfileHandler(vertx, storage, SERVER_ROOT + "/roles/v1/([^/]+)/profile");
                roleProfileHandler.enableResourceLogging(true);

                QueueClient queueClient = new QueueClient(vertx, monitoringHandler);
                reducedPropagationManager = new ReducedPropagationManager(vertx, new RedisReducedPropagationStorage(redisProvider, exceptionFactory),
                        queueClient, lock, exceptionFactory);
                reducedPropagationManager.startExpiredQueueProcessing(5000);

                queueSplitter = new QueueSplitterImpl(configurationResourceManager, SERVER_ROOT + "/admin/v1/queueSplitters");
                queueSplitter.initialize();

                hookHandler = new HookHandler(vertx, selfClient, storage, loggingResourceManager, logAppenderRepository,
                        monitoringHandler,SERVER_ROOT + "/users/v1/%s/profile",
                        SERVER_ROOT + "/hooks/v1/", queueClient,false, reducedPropagationManager, null, storage, Router.DEFAULT_ROUTER_MULTIPLIER, queueSplitter);
                hookHandler.enableResourceLogging(true);

                authorizer = new Authorizer(vertx, storage, SERVER_ROOT + "/security/v1/", ROLE_PATTERN, ROLE_PREFIX, props);
                authorizer.enableResourceLogging(true);

                validationResourceManager = new ValidationResourceManager(vertx, storage, SERVER_ROOT + "/admin/v1/validation");
                validationResourceManager.enableResourceLogging(true);
                validationSchemaProvider = new DefaultValidationSchemaProvider(vertx, new ClientRequestCreator(selfClient), Duration.ofSeconds(30));
                validator = new Validator(storage, ROOT + "/schemas/apis/", validationSchemaProvider);
                validationHandler = new ValidationHandler(validationResourceManager, selfClient, validator);

                KafkaProducerRepository kafkaProducerRepository = new KafkaProducerRepository(vertx);
                KafkaMessageSender kafkaMessageSender = new KafkaMessageSender();
                KafkaMessageValidator messageValidator = new KafkaMessageValidator(validationResourceManager, validator);

                kafkaHandler = KafkaHandler.builder()
                        .withVertx(vertx)
                        .withConfigurationResourceManager(configurationResourceManager)
                        .withKafkaMessageValidator(messageValidator)
                        .withRepository(kafkaProducerRepository)
                        .withKafkaMessageSender(kafkaMessageSender)
                        .withConfigResourceUri(SERVER_ROOT + "/admin/v1/kafka/topicsConfig")
                        .withStreamingPath(SERVER_ROOT + "/streaming/")
                        .build();

                kafkaHandler.initialize();

                schedulerResourceManager = new SchedulerResourceManager(vertx, redisProvider, storage, monitoringHandler,
                        SERVER_ROOT + "/admin/v1/schedulers");
                schedulerResourceManager.enableResourceLogging(true);

                zipExtractHandler = new ZipExtractHandler(selfClient);

                delegateHandler = new DelegateHandler(vertx, selfClient, storage,
                        SERVER_ROOT + "/admin/v1/delegates/", props, null);
                delegateHandler.enableResourceLogging(true);

                customHttpResponseHandler = new CustomHttpResponseHandler(RETURN_HTTP_STATUS_ROOT);

                router = Router.builder()
                        .withServerPath(SERVER_ROOT)
                        .withRulesPath(SERVER_ROOT + "/admin/v1/routing/rules")
                        .withUserProfilePath(SERVER_ROOT + "/users/v1/%s/profile")
                        .withVertx(vertx)
                        .withSelfClient(selfClient)
                        .withStorage(storage).withStoragePort(storagePort)
                        .withInfo(info)
                        .withMonitoringHandler(monitoringHandler)
                        .withLoggingResourceManager(loggingResourceManager)
                        .withLogAppenderRepository(logAppenderRepository)
                        .withResourceLogging(true)
                        .withRoutingConfiguration(configurationResourceManager, SERVER_ROOT + "/admin/v1/routing/config")
                        .withHttpClientFactory(this::createHttpClientForRouter)
                        .withOAuthProvider(new DefaultOAuthProvider(vertx))
                        .addDoneHandler(aVoid -> {
                            hookHandler.init();
                            delegateHandler.init();
                        })
                        .build();

                QueueCircuitBreakerRulePatternToCircuitMapping rulePatternToCircuitMapping = new QueueCircuitBreakerRulePatternToCircuitMapping();

                queueCircuitBreakerConfigurationResourceManager = new QueueCircuitBreakerConfigurationResourceManager(vertx,
                        storage, SERVER_ROOT + "/admin/v1/circuitbreaker");
                queueCircuitBreakerConfigurationResourceManager.enableResourceLogging(true);
                QueueCircuitBreakerStorage queueCircuitBreakerStorage = new RedisQueueCircuitBreakerStorage(redisProvider, exceptionFactory);
                QueueCircuitBreakerHttpRequestHandler requestHandler = new QueueCircuitBreakerHttpRequestHandler(vertx, queueCircuitBreakerStorage,
                        SERVER_ROOT + "/queuecircuitbreaker/circuit");

                QueueCircuitBreaker queueCircuitBreaker = new QueueCircuitBreakerImpl(vertx, lock,
                        Address.redisquesAddress(), queueCircuitBreakerStorage, ruleProvider, exceptionFactory, rulePatternToCircuitMapping,
                        queueCircuitBreakerConfigurationResourceManager, requestHandler, circuitBreakerPort);

                new QueueProcessor(vertx, selfClient, monitoringHandler, queueCircuitBreaker);

                LogController logController = new LogController();
                logController.registerLogConfiguratorMBean(JMX_DOMAIN);

                ResetMetricsController resetMetricsController = new ResetMetricsController(vertx);
                resetMetricsController.registerResetMetricsControlMBean(JMX_DOMAIN, PREFIX);

                RunConfig runConfig = RunConfig.with()
                        .authorizer(authorizer)
                        .validationResourceManager(validationResourceManager)
                        .validationHandler(validationHandler)
                        .cacheHandler(cacheHandler)
                        .corsHandler(corsHandler)
                        .deltaHandler(deltaHandler)
                        .expansionHandler(expansionHandler)
                        .hookHandler(hookHandler)
                        .qosHandler(qosHandler)
                        .copyResourceHandler(copyResourceHandler)
                        .eventBusHandler(eventBusHandler)
                        .kafkaHandler(kafkaHandler)
                        .roleProfileHandler(roleProfileHandler)
                        .userProfileHandler(userProfileHandler)
                        .loggingResourceManager(loggingResourceManager)
                        .configurationResourceManager(configurationResourceManager)
                        .queueCircuitBreakerConfigurationResourceManager(queueCircuitBreakerConfigurationResourceManager)
                        .queueSplitter(queueSplitter)
                        .schedulerResourceManager(schedulerResourceManager)
                        .zipExtractHandler(zipExtractHandler)
                        .delegateHandler(delegateHandler)
                        .customHttpResponseHandler(customHttpResponseHandler)
                        .contentTypeConstraintHandler(contentTypeConstraintHandler)
                        .build(vertx, redisProvider, Server.class, router, monitoringHandler);
                Handler routingContextHandlerrNew = runConfig.buildRoutingContextHandler();
                selfClient.setRoutingContexttHandler(routingContextHandlerrNew);

                HttpServerOptions options = new HttpServerOptions();
                // till vertx2 100-continues was performed automatically (per default),
                // since vertx3 it is off per default.
                options.setHandle100ContinueAutomatically(true);

                mainServer = vertx.createHttpServer(options);
                io.vertx.ext.web.Router vertxRouter = io.vertx.ext.web.Router.router(vertx);
                eventBusHandler.install(vertxRouter);
                vertxRouter.route().handler(routingContextHandlerrNew);
                mainServer.requestHandler(vertxRouter);
                mainServer.listen(mainPort);
            }
        });
    }

    private HttpClient createHttpClientForRouter(HttpClientOptions opts) {
        // Setup an original vertx http client.
        HttpClient client = vertx.createHttpClient(opts);
        // But decorate it for advanced close handling
        client = new DeferCloseHttpClient(vertx, client);
        return client;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy