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

org.graylog2.plugin.inject.Graylog2Module Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program 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
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */
package org.graylog2.plugin.inject;

import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.google.common.util.concurrent.Service;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.OptionalBinder;
import com.google.inject.name.Names;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.graylog.plugins.views.search.views.ViewResolver;
import org.graylog2.audit.AuditEventSender;
import org.graylog2.audit.AuditEventType;
import org.graylog2.audit.PluginAuditEventTypes;
import org.graylog2.audit.formatter.AuditEventFormatter;
import org.graylog2.bootstrap.preflight.PreflightCheck;
import org.graylog2.contentpacks.constraints.ConstraintChecker;
import org.graylog2.contentpacks.facades.EntityWithExcerptFacade;
import org.graylog2.contentpacks.model.ModelType;
import org.graylog2.migrations.Migration;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.decorators.SearchResponseDecorator;
import org.graylog2.plugin.indexer.retention.RetentionStrategy;
import org.graylog2.plugin.indexer.rotation.RotationStrategy;
import org.graylog2.plugin.inputs.MessageInput;
import org.graylog2.plugin.inputs.annotations.ConfigClass;
import org.graylog2.plugin.inputs.annotations.FactoryClass;
import org.graylog2.plugin.inputs.codecs.Codec;
import org.graylog2.plugin.inputs.transports.Transport;
import org.graylog2.plugin.lookup.LookupCache;
import org.graylog2.plugin.lookup.LookupCacheConfiguration;
import org.graylog2.plugin.lookup.LookupDataAdapter;
import org.graylog2.plugin.lookup.LookupDataAdapterConfiguration;
import org.graylog2.plugin.outputs.MessageOutput;
import org.graylog2.plugin.security.PasswordAlgorithm;
import org.graylog2.plugin.security.PluginPermissions;
import org.graylog2.plugin.validate.ClusterConfigValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.ext.ExceptionMapper;
import java.lang.annotation.Annotation;

public abstract class Graylog2Module extends AbstractModule {
    private static final Logger LOG = LoggerFactory.getLogger(Graylog2Module.class);

    public static final String SYSTEM_REST_RESOURCES = "systemRestResources";

    protected void installTransport(
            MapBinder> mapBinder,
            String name,
            Class transportClass) {

        final Class configClass =
                (Class)
                        findInnerClassAnnotatedWith(ConfigClass.class, transportClass, Transport.Config.class);

        final Class> factoryClass =
                (Class>)
                        findInnerClassAnnotatedWith(FactoryClass.class, transportClass, Transport.Factory.class);

        if (configClass == null) {
            LOG.error("Unable to find an inner class annotated with @ConfigClass in transport {}. This transport will not be available!",
                    transportClass);
            return;
        }
        if (factoryClass == null) {
            LOG.error("Unable to find an inner class annotated with @FactoryClass in transport {}. This transport will not be available!",
                    transportClass);
            return;
        }
        installTransport(mapBinder, name, transportClass, configClass, factoryClass);
    }

    protected void installTransport(
            MapBinder> mapBinder,
            String name,
            Class transportClass,
            Class configClass,
            Class> factoryClass) {
        final Key> factoryKey = Key.get(factoryClass);
        install(new FactoryModuleBuilder()
                .implement(Transport.class, transportClass)
                .implement(Transport.Config.class, configClass)
                .build(factoryClass));

        mapBinder.addBinding(name).to(factoryKey);
    }

    protected void installCodec(MapBinder> mapBinder, Class codecClass) {
        if (codecClass.isAnnotationPresent(org.graylog2.plugin.inputs.annotations.Codec.class)) {
            final org.graylog2.plugin.inputs.annotations.Codec a = codecClass.getAnnotation(org.graylog2.plugin.inputs.annotations.Codec.class);
            installCodec(mapBinder, a.name(), codecClass);
        } else {
            LOG.error("Codec {} not annotated with {}. Cannot determine its id. This is a bug, please use that annotation, this codec will not be available",
                    codecClass, org.graylog2.plugin.inputs.annotations.Codec.class);
        }
    }

    protected void installCodec(
            MapBinder> mapBinder,
            String name,
            Class codecClass) {

        final Class configClass =
                (Class)
                        findInnerClassAnnotatedWith(ConfigClass.class, codecClass, Codec.Config.class);

        final Class> factoryClass =
                (Class>)
                        findInnerClassAnnotatedWith(FactoryClass.class, codecClass, Codec.Factory.class);

        if (configClass == null) {
            LOG.error("Unable to find an inner class annotated with @ConfigClass in codec {}. This codec will not be available!",
                    codecClass);
            return;
        }
        if (factoryClass == null) {
            LOG.error("Unable to find an inner class annotated with @FactoryClass in codec {}. This codec will not be available!",
                    codecClass);
            return;
        }
        installCodec(mapBinder, name, codecClass, configClass, factoryClass);
    }

    protected void installCodec(
            MapBinder> mapBinder,
            String name,
            Class codecClass,
            Class configClass,
            Class> factoryClass) {

        final Key> factoryKey = Key.get(factoryClass);

        install(new FactoryModuleBuilder()
                .implement(Codec.class, codecClass)
                .implement(Codec.Config.class, configClass)
                .build(factoryClass));

        mapBinder.addBinding(name).to(factoryKey);
    }

    @Nullable
    protected Class findInnerClassAnnotatedWith(Class annotationClass,
                                                   Class containingClass,
                                                   Class targetClass) {
        final Class[] declaredClasses = containingClass.getDeclaredClasses();
        Class annotatedClass = null;
        for (final Class declaredClass : declaredClasses) {
            if (!declaredClass.isAnnotationPresent(annotationClass)) {
                continue;
            }
            if (targetClass.isAssignableFrom(declaredClass)) {
                if (annotatedClass != null) {
                    LOG.error("Multiple annotations for {} found in {}. This is invalid.", annotatedClass.getSimpleName(), containingClass);
                    return null;
                }
                annotatedClass = declaredClass;
            } else {
                LOG.error("{} annotated as {} is not extending the expected {}. Did you forget to implement the correct interface?",
                        declaredClass, annotationClass.getSimpleName(), targetClass);
                return null;
            }
        }
        return annotatedClass;
    }

    protected MapBinder> codecMapBinder() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral>() {
                });
    }

    protected MapBinder> transportMapBinder() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral>() {
                });
    }

    protected MapBinder> inputsMapBinder() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral>() {
                });
    }

    protected MapBinder rotationStrategiesMapBinder() {
        return MapBinder.newMapBinder(binder(), String.class, RotationStrategy.class);
    }

    protected MapBinder retentionStrategyMapBinder() {
        return MapBinder.newMapBinder(binder(), String.class, RetentionStrategy.class);
    }

    protected void installRotationStrategy(MapBinder mapBinder, Class target) {
        mapBinder.addBinding(target.getCanonicalName()).to(target);
    }

    protected void installRetentionStrategy(MapBinder mapBinder, Class target) {
        mapBinder.addBinding(target.getCanonicalName()).to(target);
    }

    protected  void installInput(MapBinder> inputMapBinder,
                                                         Class target,
                                                         Class> targetFactory) {
        install(new FactoryModuleBuilder().implement(MessageInput.class, target).build(targetFactory));
        inputMapBinder.addBinding(target.getCanonicalName()).to(Key.get(targetFactory));
    }

    protected  void installInput(MapBinder> inputMapBinder,
                                                         Class target) {
        Class> factoryClass =
                (Class>) findInnerClassAnnotatedWith(FactoryClass.class, target, MessageInput.Factory.class);

        if (factoryClass == null) {
            LOG.error("Unable to find an inner class annotated with @FactoryClass in input {}. This input will not be available!", target);
            return;
        }

        installInput(inputMapBinder, target, factoryClass);
    }

    // This should only be used by plugins that have been built before Graylog 3.0.1.
    // See comments in MessageOutput.Factory and MessageOutput.Factory2 for details
    protected MapBinder> outputsMapBinder() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral>() {
                });
    }

    // This should only be used by plugins that have been built before Graylog 3.0.1.
    // See comments in MessageOutput.Factory and MessageOutput.Factory2 for details
    protected  void installOutput(MapBinder> outputMapBinder,
                                                           Class target,
                                                           Class> targetFactory) {
        install(new FactoryModuleBuilder().implement(MessageOutput.class, target).build(targetFactory));
        outputMapBinder.addBinding(target.getCanonicalName()).to(Key.get(targetFactory));
    }

    // This should be used by plugins that have been built for 3.0.1 or later.
    // See comments in MessageOutput.Factory and MessageOutput.Factory2 for details
    protected MapBinder> outputsMapBinder2() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral>() {
                });
    }

    // This should be used by plugins that have been built for 3.0.1 or later.
    // See comments in MessageOutput.Factory and MessageOutput.Factory2 for details
    protected  void installOutput2(MapBinder> outputMapBinder,
                                                            Class target,
                                                            Class> targetFactory) {
        install(new FactoryModuleBuilder().implement(MessageOutput.class, target).build(targetFactory));
        outputMapBinder.addBinding(target.getCanonicalName()).to(Key.get(targetFactory));
    }

    protected  void installOutput(MapBinder> outputMapBinder,
                                                           Class target) {
        Class> factoryClass =
                (Class>) findInnerClassAnnotatedWith(FactoryClass.class, target, MessageOutput.Factory.class);

        if (factoryClass == null) {
            LOG.error("Unable to find an inner class annotated with @FactoryClass in output {}. This output will not be available!", target);
            return;
        }

        installOutput(outputMapBinder, target, factoryClass);
    }

    protected Multibinder permissionsBinder() {
        return Multibinder.newSetBinder(binder(), PluginPermissions.class);
    }

    protected void installPermissions(Multibinder classMultibinder,
                                      Class permissionsClass) {
        classMultibinder.addBinding().to(permissionsClass);
    }

    protected Multibinder auditEventTypesBinder() {
        return Multibinder.newSetBinder(binder(), PluginAuditEventTypes.class);
    }

    protected void installAuditEventTypes(Multibinder classMultibinder,
                                          Class auditEventTypesClass) {
        classMultibinder.addBinding().to(auditEventTypesClass);
    }

    protected MapBinder auditEventFormatterMapBinder() {
        return MapBinder.newMapBinder(binder(), AuditEventType.class, AuditEventFormatter.class);
    }

    protected void installAuditEventFormatter(MapBinder auditEventFormatterMapBinder,
                                              AuditEventType auditEventType,
                                              Class auditEventFormatter) {
        auditEventFormatterMapBinder.addBinding(auditEventType).to(auditEventFormatter);
    }

    protected OptionalBinder auditEventSenderBinder() {
        return OptionalBinder.newOptionalBinder(binder(), AuditEventSender.class);
    }


    @Nonnull
    protected Multibinder> jerseyDynamicFeatureBinder() {
        return Multibinder.newSetBinder(binder(), new DynamicFeatureType());
    }

    @Nonnull
    protected Multibinder> jerseyContainerResponseFilterBinder() {
        return Multibinder.newSetBinder(binder(), new ContainerResponseFilterType());
    }

    @Nonnull
    protected Multibinder> jerseyExceptionMapperBinder() {
        return Multibinder.newSetBinder(binder(), new ExceptionMapperType());
    }

    @Nonnull
    protected Multibinder jerseyAdditionalComponentsBinder() {
        return Multibinder.newSetBinder(binder(), Class.class, Names.named("additionalJerseyComponents"));
    }

    protected Multibinder serviceBinder() {
        return Multibinder.newSetBinder(binder(), Service.class);
    }

    protected MapBinder passwordAlgorithmBinder() {
        return MapBinder.newMapBinder(binder(), String.class, PasswordAlgorithm.class);
    }

    protected MapBinder authenticationRealmBinder() {
        return MapBinder.newMapBinder(binder(), String.class, AuthenticatingRealm.class);
    }

    protected MapBinder authorizationOnlyRealmBinder() {
        return MapBinder.newMapBinder(binder(), String.class, AuthorizingRealm.class);
    }

    protected MapBinder preflightChecksBinder() {
        return MapBinder.newMapBinder(binder(), String.class, PreflightCheck.class);
    }

    protected void addPreflightCheck(Class preflightCheck) {
        preflightChecksBinder().addBinding(preflightCheck.getCanonicalName()).to(preflightCheck);
    }

    protected MapBinder searchResponseDecoratorBinder() {
        return MapBinder.newMapBinder(binder(), String.class, SearchResponseDecorator.Factory.class);
    }

    protected void installSearchResponseDecorator(MapBinder searchResponseDecoratorBinder,
                                                  Class searchResponseDecoratorClass,
                                                  Class searchResponseDecoratorFactoryClass) {
        install(new FactoryModuleBuilder().implement(SearchResponseDecorator.class, searchResponseDecoratorClass).build(searchResponseDecoratorFactoryClass));
        searchResponseDecoratorBinder.addBinding(searchResponseDecoratorClass.getCanonicalName()).to(searchResponseDecoratorFactoryClass);
    }

    protected MapBinder alertConditionBinder() {
        return MapBinder.newMapBinder(binder(), String.class, AlertCondition.Factory.class);
    }

    protected void installAlertCondition(MapBinder alertConditionBinder,
                                         Class alertConditionClass,
                                         Class alertConditionFactoryClass) {
        install(new FactoryModuleBuilder().implement(AlertCondition.class, alertConditionClass).build(alertConditionFactoryClass));
        alertConditionBinder.addBinding(alertConditionClass.getCanonicalName()).to(alertConditionFactoryClass);
    }

    protected void installAlertConditionWithCustomName(MapBinder alertConditionBinder,
                                                       String identifier,
                                                       Class alertConditionClass,
                                                       Class alertConditionFactoryClass) {
        install(new FactoryModuleBuilder().implement(AlertCondition.class, alertConditionClass).build(alertConditionFactoryClass));
        alertConditionBinder.addBinding(identifier).to(alertConditionFactoryClass);
    }

    protected MapBinder lookupCacheBinder() {
        return MapBinder.newMapBinder(binder(), String.class, LookupCache.Factory.class);
    }

    protected void installLookupCache(String name,
                                      Class cacheClass,
                                      Class factoryClass,
                                      Class configClass) {
        install(new FactoryModuleBuilder().implement(LookupCache.class, cacheClass).build(factoryClass));
        lookupCacheBinder().addBinding(name).to(factoryClass);
        registerJacksonSubtype(configClass, name);
    }


    protected MapBinder lookupDataAdapterBinder() {
        return MapBinder.newMapBinder(binder(), String.class, LookupDataAdapter.Factory.class);
    }

    protected MapBinder lookupDataAdapterBinder2() {
        return MapBinder.newMapBinder(binder(), String.class, LookupDataAdapter.Factory2.class);
    }

    protected void installLookupDataAdapter(String name,
                                            Class adapterClass,
                                            Class factoryClass,
                                            Class configClass) {
        install(new FactoryModuleBuilder().implement(LookupDataAdapter.class, adapterClass).build(factoryClass));
        lookupDataAdapterBinder().addBinding(name).to(factoryClass);
        registerJacksonSubtype(configClass, name);
    }

    protected void installLookupDataAdapter2(String name,
                                             Class adapterClass,
                                             Class factoryClass,
                                             Class configClass) {
        install(new FactoryModuleBuilder().implement(LookupDataAdapter.class, adapterClass).build(factoryClass));
        lookupDataAdapterBinder2().addBinding(name).to(factoryClass);
        registerJacksonSubtype(configClass, name);
    }

    /**
     * Prefer using {@link #registerJacksonSubtype(Class)} or {@link #registerJacksonSubtype(Class, String)}.
     */
    protected Multibinder jacksonSubTypesBinder() {
        return Multibinder.newSetBinder(binder(), NamedType.class, JacksonSubTypes.class);
    }

    /**
     * Use this if the class itself is annotated by {@link com.fasterxml.jackson.annotation.JsonTypeName} instead of explicitly given.
     *
     * @param klass
     */
    protected void registerJacksonSubtype(Class klass) {
        registerJacksonSubtype(klass, null);
    }

    /**
     * Use this if the class does not have a {@link com.fasterxml.jackson.annotation.JsonTypeName} annotation.
     *
     * @param klass
     * @param name
     */
    protected void registerJacksonSubtype(Class klass, String name) {
        jacksonSubTypesBinder().addBinding().toInstance(new NamedType(klass, name));
    }

    protected Multibinder migrationsBinder() {
        return Multibinder.newSetBinder(binder(), Migration.class);
    }

    protected MapBinder> entityFacadeBinder() {
        return MapBinder.newMapBinder(binder(), new TypeLiteral() {}, new TypeLiteral>() {});
    }

    protected Multibinder constraintCheckerBinder() {
        return Multibinder.newSetBinder(binder(), ConstraintChecker.class);
    }

    private static class DynamicFeatureType extends TypeLiteral> {}

    private static class ContainerResponseFilterType extends TypeLiteral> {}

    private static class ExceptionMapperType extends TypeLiteral> {}

    /**
     * Adds given API resource as a system resource. This should not be used from plugins!
     * Plugins should use {@link org.graylog2.plugin.PluginModule#addRestResource(Class)} instead to ensure the
     * addition of the path prefix.
     *
     * @param restResourceClass the resource to add
     */
    protected void addSystemRestResource(Class restResourceClass) {
        systemRestResourceBinder().addBinding().toInstance(restResourceClass);
    }

    private Multibinder> systemRestResourceBinder() {
        return Multibinder.newSetBinder(
                binder(),
                new TypeLiteral>() {},
                Names.named(SYSTEM_REST_RESOURCES)
        );
    }

    protected MapBinder, ClusterConfigValidator> clusterConfigMapBinder() {
        TypeLiteral> keyType = new TypeLiteral>() {};
        TypeLiteral valueType = new TypeLiteral() {};
        return MapBinder.newMapBinder(binder(), keyType, valueType);
    }

    protected MapBinder viewResolverBinder() {
        return MapBinder.newMapBinder(binder(),
                TypeLiteral.get(String.class),
                new TypeLiteral() {});
    }

    protected void installViewResolver(String name,
                                       Class resolverClass) {
        viewResolverBinder().addBinding(name).to(resolverClass).asEagerSingleton();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy