/*
* Copyright 2015-2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hawkular.alerts.engine.impl;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.hawkular.alerts.api.services.ActionListener;
import org.hawkular.alerts.api.services.DefinitionsEvent;
import org.hawkular.alerts.api.services.DefinitionsEvent.Type;
import org.hawkular.alerts.api.services.DefinitionsListener;
import org.hawkular.alerts.api.services.DistributedEvent;
import org.hawkular.alerts.api.services.DistributedListener;
import org.hawkular.alerts.engine.service.PartitionManager;
import org.hawkular.alerts.engine.service.PartitionTriggerListener;
import org.jboss.logging.Logger;
/**
* Register DefinitionListener and ActionListener instances.
* Store initialization state of the whole Alerts engine.
*
* @author Jay Shaughnessy
* @author Lucas Ponce
*/
@Startup
@Singleton
public class AlertsContext {
private final Logger log = Logger.getLogger(AlertsContext.class);
private Map<DefinitionsListener, Set<Type>> definitionListeners = new HashMap<>();
List<ActionListener> actionsListeners = new CopyOnWriteArrayList<>();
List<DistributedListener> distributedListener = new CopyOnWriteArrayList<>();
private boolean distributed = false;
@EJB
PartitionManager partitionManager;
@PostConstruct
public void init() {
if (partitionManager != null) {
distributed = partitionManager.isDistributed();
}
if (distributed) {
partitionManager.registerTriggerListener(new PartitionTriggerListener() {
@Override
public void onTriggerChange(PartitionManager.Operation operation, String tenantId, String triggerId) {
DistributedEvent.Operation op = DistributedEvent.Operation.valueOf(operation.name());
DistributedEvent event = new DistributedEvent(op, tenantId, triggerId);
distributedListener.stream().forEach(listener -> listener.onChange(Collections.singleton(event)));
}
@Override
public void onPartitionChange(Map<String, List<String>> partition, Map<String, List<String>> removed,
Map<String, List<String>> added) {
Set<DistributedEvent> events = new HashSet<>();
removed.entrySet().stream().forEach(entry -> {
String tenantId = entry.getKey();
entry.getValue().stream().forEach(triggerId ->
events.add(new DistributedEvent(DistributedEvent.Operation.REMOVE, tenantId, triggerId))
);
});
added.entrySet().stream().forEach(entry -> {
String tenantId = entry.getKey();
entry.getValue().stream().forEach(triggerId ->
events.add(new DistributedEvent(DistributedEvent.Operation.ADD, tenantId, triggerId))
);
});
distributedListener.stream().forEach(listener -> listener.onChange(events));
}
});
}
}
public void registerDefinitionListener(DefinitionsListener listener, Type eventType, Type... eventTypes) {
EnumSet<Type> types = EnumSet.of(eventType, eventTypes);
if (log.isDebugEnabled()) {
log.debug("Registering listeners " + listener + " for event types " + types);
}
definitionListeners.put(listener, types);
}
public void registerDistributedListener(DistributedListener listener) {
if (listener == null) {
throw new IllegalArgumentException("DistributedListener must not be null");
}
distributedListener.add(listener);
}
public Map<DefinitionsListener, Set<Type>> getDefinitionListeners() {
return definitionListeners;
}
public void registerActionListener(ActionListener listener) {
if (listener == null) {
throw new IllegalArgumentException("ActionListener must not be null");
}
actionsListeners.add(listener);
}
public List<ActionListener> getActionsListeners() {
return actionsListeners;
}
public void notifyListeners(List<DefinitionsEvent> notifications) {
Set<DefinitionsEvent.Type> notificationTypes = notifications.stream()
.map(n -> n.getType())
.collect(Collectors.toSet());
log.debugf("Notifying applicable listeners %s of events %s", definitionListeners, notifications);
definitionListeners.entrySet().stream()
.filter(e -> shouldNotify(e.getValue(), notificationTypes))
.forEach(e -> {
log.debugf("Notified Listener %s of %s", e.getKey(), notificationTypes);
e.getKey().onChange(notifications.stream()
.filter(de -> e.getValue().contains(de.getType()))
.collect(Collectors.toList()));
});
if (!distributed) {
distributedListener.stream().forEach(listener -> listener.onChange(mapDistributedEvents(notifications)));
}
}
private boolean shouldNotify(Set<DefinitionsEvent.Type> listenerTypes, Set<DefinitionsEvent.Type> eventTypes) {
HashSet<Type> intersection = new HashSet<>(listenerTypes);
intersection.retainAll(eventTypes);
return !intersection.isEmpty();
}
private Set<DistributedEvent> mapDistributedEvents(List<DefinitionsEvent> notification) {
if (notification == null) {
return null;
}
Set<DistributedEvent> events = new LinkedHashSet<>();
for (DefinitionsEvent definitionsEvent : notification) {
DistributedEvent.Operation op = null;
String tenantId = definitionsEvent.getTargetTenantId();
String triggerId = definitionsEvent.getTargetId();
switch (definitionsEvent.getType()) {
case TRIGGER_CREATE:
op = DistributedEvent.Operation.ADD;
break;
case TRIGGER_UPDATE:
op = DistributedEvent.Operation.UPDATE;
break;
case TRIGGER_REMOVE:
op = DistributedEvent.Operation.REMOVE;
break;
case TRIGGER_CONDITION_CHANGE:
op = DistributedEvent.Operation.UPDATE;
break;
}
if (op != null) {
events.add(new DistributedEvent(op, tenantId, triggerId));
}
}
return events;
}
}