/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.ambari.server.events.listeners.alerts; import java.text.MessageFormat; import java.util.List; import java.util.Set; import java.util.concurrent.locks.Lock; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.EagerSingleton; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.controller.AmbariServer; import org.apache.ambari.server.controller.ControllerModule; import org.apache.ambari.server.events.ServiceInstalledEvent; import org.apache.ambari.server.events.ServiceRemovedEvent; import org.apache.ambari.server.events.publishers.AmbariEventPublisher; import org.apache.ambari.server.orm.GuiceJpaInitializer; import org.apache.ambari.server.orm.dao.AlertDefinitionDAO; import org.apache.ambari.server.orm.dao.AlertDispatchDAO; import org.apache.ambari.server.orm.entities.AlertDefinitionEntity; import org.apache.ambari.server.orm.entities.AlertGroupEntity; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.alert.AlertDefinition; import org.apache.ambari.server.state.alert.AlertDefinitionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.Subscribe; import com.google.common.util.concurrent.Striped; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * The {@link AlertServiceStateListener} class handles * {@link ServiceInstalledEvent} and {@link ServiceRemovedEvent} and ensures * that {@link AlertDefinitionEntity} and {@link AlertGroupEntity} instances are * correctly populated or cleaned up. */ @Singleton @EagerSingleton public class AlertServiceStateListener { /** * Logger. */ private static Logger LOG = LoggerFactory.getLogger(AlertServiceStateListener.class); /** * Services metainfo; injected lazily as a {@link Provider} since JPA is not * fully initialized when this singleton is eagerly instantiated. See * {@link AmbariServer#main(String[])} and the ordering of * {@link ControllerModule} and {@link GuiceJpaInitializer}. */ @Inject private Provider<AmbariMetaInfo> m_metaInfoProvider; /** * Used when a service is installed to read alert definitions from the stack * and coerce them into {@link AlertDefinitionEntity}. */ @Inject private AlertDefinitionFactory m_alertDefinitionFactory; /** * Used when a service is installed to insert a default * {@link AlertGroupEntity} into the database. */ @Inject private AlertDispatchDAO m_alertDispatchDao; /** * Used when a service is installed to insert {@link AlertDefinitionEntity} * into the database or when a service is removed to delete the definition. */ @Inject private AlertDefinitionDAO m_definitionDao; /** * Used to retrieve a cluster using clusterId from event. */ @Inject private Provider<Clusters> m_clusters; /** * Used for ensuring that the concurrent nature of the event handler methods * don't collide when attempting to perform operations on the same service. */ private Striped<Lock> m_locksByService = Striped.lazyWeakLock(20); /** * Constructor. * * @param publisher */ @Inject public AlertServiceStateListener(AmbariEventPublisher publisher) { publisher.register(this); } /** * Handles service installed events by populating the database with all known * alert definitions for the newly installed service and creates the service's * default alert group. * * @param event * the published event being handled (not {@code null}). */ @Subscribe @AllowConcurrentEvents public void onAmbariEvent(ServiceInstalledEvent event) { LOG.debug("Received event {}", event); long clusterId = event.getClusterId(); String stackName = event.getStackName(); String stackVersion = event.getStackVersion(); String serviceName = event.getServiceName(); Lock lock = m_locksByService.get(serviceName); lock.lock(); try { // create the default alert group for the new service if absent; this MUST // be done before adding definitions so that they are properly added to the // default group if (null == m_alertDispatchDao.findDefaultServiceGroup(clusterId, serviceName)) { try { m_alertDispatchDao.createDefaultGroup(clusterId, serviceName); } catch (AmbariException ambariException) { LOG.error("Unable to create a default alert group for {}", event.getServiceName(), ambariException); } } // populate alert definitions for the new service from the database, but // don't worry about sending down commands to the agents; the host // components are not yet bound to the hosts so we'd have no way of knowing // which hosts are invalidated; do that in another impl try { Set<AlertDefinition> alertDefinitions = m_metaInfoProvider.get().getAlertDefinitions( stackName, stackVersion, serviceName); for (AlertDefinition definition : alertDefinitions) { AlertDefinitionEntity entity = m_alertDefinitionFactory.coerce( clusterId, definition); m_definitionDao.create(entity); } } catch (AmbariException ae) { String message = MessageFormat.format( "Unable to populate alert definitions from the database during installation of {0}", serviceName); LOG.error(message, ae); } } finally { lock.unlock(); } } /** * Removes all alert data associated with the removed serviced. * * @param event * the published event being handled (not {@code null}). */ @Subscribe @AllowConcurrentEvents public void onAmbariEvent(ServiceRemovedEvent event) { LOG.debug("Received event {}", event); try { m_clusters.get().getClusterById(event.getClusterId()); } catch (AmbariException e) { LOG.warn("Unable to retrieve cluster with id {}", event.getClusterId()); return; } String serviceName = event.getServiceName(); Lock lock = m_locksByService.get(serviceName); lock.lock(); try { List<AlertDefinitionEntity> definitions = m_definitionDao.findByService(event.getClusterId(), event.getServiceName()); for (AlertDefinitionEntity definition : definitions) { try { m_definitionDao.remove(definition); } catch (Exception exception) { LOG.error("Unable to remove alert definition {}", definition.getDefinitionName(), exception); } } // remove the default group for the service AlertGroupEntity group = m_alertDispatchDao.findGroupByName(event.getClusterId(), event.getServiceName()); if (null != group && group.isDefault()) { try { m_alertDispatchDao.remove(group); } catch (Exception exception) { LOG.error("Unable to remove default alert group {}", group.getGroupName(), exception); } } } finally { lock.unlock(); } } }