org.graylog.events.context.EventDefinitionContextService Maven / Gradle / Ivy
/*
* 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.graylog.events.context;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import org.graylog.events.notifications.EventNotificationExecutionJob;
import org.graylog.events.processor.EventDefinitionDto;
import org.graylog.events.processor.EventProcessorExecutionJob;
import org.graylog.scheduler.DBJobDefinitionService;
import org.graylog.scheduler.DBJobTriggerService;
import org.graylog.scheduler.JobDefinitionDto;
import org.graylog.scheduler.JobTriggerData;
import org.graylog.scheduler.JobTriggerDto;
import org.graylog.scheduler.JobTriggerStatus;
import org.joda.time.DateTime;
import org.mongojack.DBQuery;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Collects additional information for {@link org.graylog.events.processor.EventDefinition event definitions} like
* scheduler information. This allows us to return additional information for event definitions without modifying
* their DTOs.
*/
public class EventDefinitionContextService {
public static final String SCHEDULER_KEY = "scheduler";
private final DBJobDefinitionService jobDefinitionService;
private final DBJobTriggerService jobTriggerService;
@Inject
public EventDefinitionContextService(DBJobDefinitionService jobDefinitionService, DBJobTriggerService jobTriggerService) {
this.jobDefinitionService = jobDefinitionService;
this.jobTriggerService = jobTriggerService;
}
public ImmutableMap contextFor(List eventDefinitions) {
return ImmutableMap.of(SCHEDULER_KEY, schedulerContext(eventDefinitions));
}
public ImmutableMap contextFor(EventDefinitionDto eventDefinition) {
final ImmutableMap schedulerContext = schedulerContext(Collections.singletonList(eventDefinition));
return ImmutableMap.of(SCHEDULER_KEY, schedulerContext.get(eventDefinition.id()));
}
private Map> getJobDefinitions(List eventDefinitions) {
final Set eventDefinitionIds = eventDefinitions.stream().map(EventDefinitionDto::id).collect(Collectors.toSet());
return jobDefinitionService.getAllByConfigField(EventProcessorExecutionJob.Config.FIELD_EVENT_DEFINITION_ID, eventDefinitionIds);
}
private Map> getJobTriggers(Map> jobDefinitions) {
final Set jobDefinitionIds = jobDefinitions.values().stream()
.flatMap(Collection::stream)
.map(JobDefinitionDto::id)
.collect(Collectors.toSet());
return jobTriggerService.getForJobs(jobDefinitionIds);
}
private long getQueuedNotifications(EventDefinitionDto eventDefinition) {
final DBQuery.Query query = DBQuery.and(
DBQuery.is("status", JobTriggerStatus.RUNNABLE),
DBQuery.is("data.type", EventNotificationExecutionJob.TYPE_NAME),
DBQuery.is("data.event_dto.event_definition_id", eventDefinition.id()));
return jobTriggerService.countByQuery(query);
}
private ImmutableMap schedulerContext(List eventDefinitions) {
// We try to minimize database queries by fetching all required job definitions and triggers in two requests
// TODO: Use MongoDB's $lookup aggregation operator once we switch to MongoDB 4.0 to do this with a single database query
final Map> jobDefinitions = getJobDefinitions(eventDefinitions);
final Map> jobTriggers = getJobTriggers(jobDefinitions);
final ImmutableMap.Builder ctx = ImmutableMap.builder();
for (final EventDefinitionDto eventDefinition : eventDefinitions) {
if (eventDefinition.id() == null) {
// Should not happen!
throw new IllegalStateException("Event definition doesn't have an ID: " + eventDefinition);
}
if (!jobDefinitions.containsKey(eventDefinition.id())) {
ctx.put(eventDefinition.id(), SchedulerCtx.unscheduled());
continue;
}
if (jobDefinitions.get(eventDefinition.id()).size() > 1) {
throw new IllegalStateException("Cannot handle multiple job definitions for a single event definition");
}
final JobDefinitionDto jobDefinition = jobDefinitions.get(eventDefinition.id()).get(0);
// DBJobTriggerService#getForJobs currently returns only one trigger. (raises an exception otherwise)
// Once we allow multiple triggers per job definition, this code will fail.
// TODO: Fix this code for multiple triggers per job definition
final JobTriggerDto trigger = jobTriggers.get(jobDefinition.id()).get(0);
if (trigger != null) {
ctx.put(eventDefinition.id(), SchedulerCtx.scheduled(trigger, getQueuedNotifications(eventDefinition)));
}
}
return ctx.build();
}
@AutoValue
public static abstract class SchedulerCtx {
@JsonProperty("is_scheduled")
public abstract boolean isScheduled();
@JsonProperty("status")
public abstract Optional status();
@JsonProperty("next_time")
public abstract Optional nextTime();
@JsonProperty("triggered_at")
public abstract Optional triggeredAt();
@JsonProperty("queued_notifications")
public abstract long queuedNotifications();
@JsonProperty("data")
public abstract Optional data();
public static SchedulerCtx unscheduled() {
return create(false, null, 0);
}
public static SchedulerCtx scheduled(JobTriggerDto trigger, long queuedNotifications) {
return create(true, trigger, queuedNotifications);
}
private static SchedulerCtx create(boolean isScheduled, JobTriggerDto trigger, long queuedNotifications) {
final Optional optionalTrigger = Optional.ofNullable(trigger);
return new AutoValue_EventDefinitionContextService_SchedulerCtx(
isScheduled,
optionalTrigger.map(JobTriggerDto::status),
optionalTrigger.map(JobTriggerDto::nextTime),
optionalTrigger.flatMap(JobTriggerDto::triggeredAt),
queuedNotifications,
optionalTrigger.flatMap(JobTriggerDto::data)
);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy