/* * RHQ Management Platform * Copyright (C) 2005-2014 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.plugins.alertCli; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javassist.bytecode.AccessFlag; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.Index; import org.jboss.jandex.Indexer; import org.jboss.vfs.VFSUtils; import org.jboss.vfs.VirtualFile; import org.jboss.vfs.VisitorAttributes; import org.jboss.vfs.util.SuffixMatchFilter; import org.rhq.core.domain.alert.AlertDefinition; import org.rhq.core.domain.alert.notification.AlertNotification; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.Property; import org.rhq.core.domain.configuration.PropertyList; import org.rhq.core.domain.configuration.PropertyMap; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.content.PackageType; import org.rhq.core.domain.content.PackageVersion; import org.rhq.core.domain.criteria.AlertDefinitionCriteria; import org.rhq.core.domain.criteria.Criteria.Restriction; import org.rhq.core.domain.criteria.PackageVersionCriteria; import org.rhq.core.domain.resource.composite.DisambiguationReport; import org.rhq.core.domain.util.DisambiguationReportRenderer; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.core.util.IntExtractor; import org.rhq.enterprise.server.alert.AlertDefinitionManagerLocal; import org.rhq.enterprise.server.alert.AlertNotificationManagerLocal; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.content.ContentManagerLocal; import org.rhq.enterprise.server.plugin.pc.ControlFacet; import org.rhq.enterprise.server.plugin.pc.ControlResults; import org.rhq.enterprise.server.plugin.pc.ServerPluginComponent; import org.rhq.enterprise.server.plugin.pc.ServerPluginContext; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.resource.disambiguation.DefaultDisambiguationUpdateStrategies; import org.rhq.enterprise.server.util.LookupUtil; import org.rhq.enterprise.server.xmlschema.generated.serverplugin.alert.AlertPluginDescriptorType; /** * The plugin component for controlling the CLI alerts. * * @author Lukas Krejci */ public class CliComponent implements ServerPluginComponent, ControlFacet { private static final Log LOG = LogFactory.getLog(CliComponent.class); public static final String PACKAGE_TYPE_NAME = "org.rhq.enterprise.server.plugins.packagetypeCli.SERVER_SIDE_CLI_SCRIPT"; private static final String CONTROL_CHECK_ALERTS_VALIDITY = "checkAlertsValidity"; private static final String CONTROL_REASSIGN_ALERTS = "reassignAlerts"; private static final String PROP_ALERT_DEFINITION_NAME = "alertDefinitionName"; private static final String PROP_RESOURCE_PATH = "resourcePath"; private static final String PROP_RESOURCE_ID = "resourceId"; private static final String PROP_MISSING_USERS = "missingUsers"; private static final String PROP_MISSING_SCRIPTS = "missingScripts"; private static final String PROP_ALERT_DEFINITION = "alertDefinition"; private static final String PROP_ALERT_DEFINITION_ID = "alertDefinitionId"; private static final String PROP_USER_NAME = "userName"; private static final String PROP_ALERT_DEF_IDS = "alertDefIds"; private static final String PROP_SCRIPT_TIMEOUT = "scriptTimeout"; private static final String WARNING_MESSAGE_LOAD_CLASSES = "Could not load domain packages names." + " The CLI Alert Sender will only understand fully qualified domain classes names in CLI scripts."; private String pluginName; private PackageType packageType; private int scriptTimeout; private Set<String> domainPackagesNames; @Override public void initialize(ServerPluginContext context) throws Exception { pluginName = ((AlertPluginDescriptorType) context.getPluginEnvironment().getPluginDescriptor()).getShortName(); String timeoutValue = context.getPluginConfiguration() == null ? "60" : context.getPluginConfiguration() .getSimpleValue(PROP_SCRIPT_TIMEOUT, "60"); scriptTimeout = Integer.parseInt(timeoutValue); } public PackageType getScriptPackageType() { if (packageType == null) { SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager(); ContentManagerLocal cm = LookupUtil.getContentManager(); packageType = cm.findPackageType(subjectManager.getOverlord(), null, PACKAGE_TYPE_NAME); } return packageType; } public int getScriptTimeout() { return scriptTimeout; } @Override public void start() { domainPackagesNames = loadPackagesNames(); } private Set<String> loadPackagesNames() { Enumeration<URL> persistenceFileUrls; try { persistenceFileUrls = getClass().getClassLoader().getResources("META-INF/persistence.xml"); } catch (IOException e) { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES, e); return Collections.emptySet(); } URL coreDomainPersistenceFileUrl = null; while (persistenceFileUrls.hasMoreElements()) { URL url = persistenceFileUrls.nextElement(); if (url.getPath().contains("rhq-core-domain")) { if (coreDomainPersistenceFileUrl == null) { coreDomainPersistenceFileUrl = url; } else { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES + " Found more than one core domain JAR: [" + coreDomainPersistenceFileUrl + "] [" + url + "]"); return Collections.emptySet(); } } } if (coreDomainPersistenceFileUrl == null) { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES + " Core domain JAR not found"); return Collections.emptySet(); } VirtualFile virtualFile; try { virtualFile = (VirtualFile) coreDomainPersistenceFileUrl.openConnection().getContent(); } catch (IOException e) { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES, e); return Collections.emptySet(); } // up to the archive root virtualFile = virtualFile.getParent().getParent(); Indexer indexer = new Indexer(); List<VirtualFile> classChildren; try { classChildren = virtualFile.getChildren(new SuffixMatchFilter(".class", VisitorAttributes.RECURSE_LEAVES_ONLY)); } catch (IOException e) { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES, e); return Collections.emptySet(); } for (VirtualFile classFile : classChildren) { InputStream inputStream = null; try { inputStream = classFile.openStream(); indexer.index(inputStream); } catch (IOException e) { LOG.warn(WARNING_MESSAGE_LOAD_CLASSES, e); return Collections.emptySet(); } finally { VFSUtils.safeClose(inputStream); } } Index index = indexer.complete(); Collection<ClassInfo> knownClasses = index.getKnownClasses(); Set<String> domainPackagesNames = new HashSet<String>(); for (ClassInfo knownClass : knownClasses) { if (AccessFlag.isPublic((knownClass.flags()))) { String className = knownClass.name().toString(); int i = className.lastIndexOf('.'); if (i != -1) { domainPackagesNames.add(className.substring(0, i)); } } } if (LOG.isDebugEnabled()) { LOG.debug("domainPackagesNames = " + domainPackagesNames); } return domainPackagesNames; } @Override public void stop() { domainPackagesNames = null; } @Override public void shutdown() { } @Override public ControlResults invoke(String name, Configuration parameters) { ControlResults results = new ControlResults(); try { if (CONTROL_CHECK_ALERTS_VALIDITY.equals(name)) { checkAlertsValidity(results); } else if (CONTROL_REASSIGN_ALERTS.equals(name)) { reassignAlerts(parameters); } } catch (Exception e) { results.setError(e); } return results; } private void checkAlertsValidity(ControlResults results) { List<AlertNotification> allCliNotifications = getAllCliNotifications(null); Configuration resConfig = results.getComplexResults(); PropertyList missingUsersList = new PropertyList(PROP_MISSING_USERS); resConfig.put(missingUsersList); List<AlertNotification> invalidNotifs = getCliNotificationsWithInvalidUser(allCliNotifications); convertNotificationsToInvalidAlertDefResults(missingUsersList, invalidNotifs); PropertyList missingScriptsList = new PropertyList(PROP_MISSING_SCRIPTS); resConfig.put(missingScriptsList); invalidNotifs = getCliNotificationsWithInvalidPackage(allCliNotifications); convertNotificationsToInvalidAlertDefResults(missingScriptsList, invalidNotifs); //ok, now we have to obtain the resource paths. doing it out of the above loop reduces the number //of server roundtrips List<Property> allMissing = new ArrayList<Property>(); allMissing.addAll(missingUsersList.getList()); allMissing.addAll(missingScriptsList.getList()); if (allMissing.size() > 0) { ResourceManagerLocal resourceManager = LookupUtil.getResourceManager(); List<DisambiguationReport<Property>> disambiguated = resourceManager.disambiguate(allMissing, new IntExtractor<Property>() { @Override public int extract(Property object) { PropertyMap map = (PropertyMap) object; return map.getSimple(PROP_RESOURCE_ID).getIntegerValue(); } }, DefaultDisambiguationUpdateStrategies.KEEP_ALL_PARENTS); DisambiguationReportRenderer renderer = new DisambiguationReportRenderer(); for (DisambiguationReport<Property> r : disambiguated) { PropertyMap map = (PropertyMap) r.getOriginal(); String resourcePath = renderer.render(r); map.put(new PropertySimple(PROP_RESOURCE_PATH, resourcePath)); } } } private void reassignAlerts(Configuration parameters) { PropertySimple userNameProp = parameters.getSimple(PROP_USER_NAME); PropertySimple alertDefIdsProp = parameters.getSimple(PROP_ALERT_DEF_IDS); if (userNameProp == null || userNameProp.getStringValue() == null || userNameProp.getStringValue().trim().length() == 0) { throw new IllegalArgumentException("User name not specified."); } String userName = userNameProp.getStringValue(); SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager(); Subject subject = subjectManager.getSubjectByName(userName); if (subject == null) { throw new IllegalArgumentException("User '" + userName + "' doesn't exist."); } //now get the list of the alert notifications to update List<AlertNotification> notifsToReAssign; if (alertDefIdsProp == null || alertDefIdsProp.getStringValue() == null || alertDefIdsProp.getStringValue().trim().length() == 0) { notifsToReAssign = getCliNotificationsWithInvalidUser(getAllCliNotifications(null)); } else { List<Integer> alertDefIds = asIdList(alertDefIdsProp.getStringValue().split("\\s*,\\s*")); List<AlertDefinition> defs = getAlertDefinitionsWithCliNotifications(alertDefIds); notifsToReAssign = new ArrayList<AlertNotification>(); for (AlertDefinition def : defs) { for (AlertNotification cliNotif : getCliNotifications(def.getAlertNotifications())) { notifsToReAssign.add(cliNotif); } } } AlertNotificationManagerLocal notificationManager = LookupUtil.getAlertNotificationManager(); List<Integer> notifIds = asIdList(notifsToReAssign); Map<String, String> updates = Collections.singletonMap(CliSender.PROP_USER_ID, Integer.toString(subject.getId())); notificationManager.massReconfigure(notifIds, updates); } private List<AlertNotification> getCliNotifications(List<AlertNotification> notifications) { ArrayList<AlertNotification> ret = new ArrayList<AlertNotification>(); for (AlertNotification n : notifications) { if (pluginName.equals(n.getSenderName())) { ret.add(n); } } return ret; } private List<AlertDefinition> getAlertDefinitionsWithCliNotifications(Collection<Integer> alertDefIds) { SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager(); AlertDefinitionManagerLocal manager = LookupUtil.getAlertDefinitionManager(); Subject overlord = subjectManager.getOverlord(); AlertDefinitionCriteria criteria = new AlertDefinitionCriteria(); criteria.addFilterNotificationNames(pluginName); criteria.setPageControl(PageControl.getUnlimitedInstance()); criteria.fetchAlertNotifications(true); if (alertDefIds != null) { criteria.addFilterIds(alertDefIds.toArray(new Integer[alertDefIds.size()])); } return manager.findAlertDefinitionsByCriteria(overlord, criteria); } private List<AlertNotification> getAllCliNotifications(Collection<Integer> alertDefIds) { List<AlertNotification> ret = new ArrayList<AlertNotification>(); List<AlertDefinition> defs = getAlertDefinitionsWithCliNotifications(alertDefIds); for (AlertDefinition def : defs) { List<AlertNotification> notifications = def.getAlertNotifications(); ret.addAll(getCliNotifications(notifications)); } return ret; } private List<AlertNotification> getCliNotificationsWithInvalidUser(List<AlertNotification> allNotifications) { List<AlertNotification> ret = new ArrayList<AlertNotification>(); SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager(); for (AlertNotification cliNotification : allNotifications) { Subject checkSubject = null; PropertySimple subjectIdProperty = cliNotification.getConfiguration().getSimple(CliSender.PROP_USER_ID); if (subjectIdProperty != null) { int subjectId = subjectIdProperty.getIntegerValue(); checkSubject = subjectManager.getSubjectById(subjectId); } if (checkSubject == null) { ret.add(cliNotification); } } return ret; } private List<AlertNotification> getCliNotificationsWithInvalidPackage(List<AlertNotification> allNotifications) { List<AlertNotification> ret = new ArrayList<AlertNotification>(); ContentManagerLocal contentManager = LookupUtil.getContentManager(); SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager(); Subject overlord = subjectManager.getOverlord(); PackageVersionCriteria crit = new PackageVersionCriteria(); crit.setRestriction(Restriction.COUNT_ONLY); for (AlertNotification cliNotification : allNotifications) { int count = 0; String packageId = cliNotification.getConfiguration().getSimpleValue(CliSender.PROP_PACKAGE_ID, null); if (packageId != null) { crit.addFilterPackageId(Integer.valueOf(packageId)); PageList<PackageVersion> res = contentManager.findPackageVersionsByCriteria(overlord, crit); count = res.getTotalSize(); } if (count == 0) { ret.add(cliNotification); } } return ret; } private void convertNotificationsToInvalidAlertDefResults(PropertyList results, List<AlertNotification> invalidNotifications) { Set<AlertDefinition> processedDefs = new HashSet<AlertDefinition>(); for (AlertNotification cliNotification : invalidNotifications) { AlertDefinition def = cliNotification.getAlertDefinition(); if (!processedDefs.add(def)) { //skip this definition - it has more than one incorrect notifs and we already //included it in the output. continue; } PropertyMap alertDefinitionMap = new PropertyMap(PROP_ALERT_DEFINITION); alertDefinitionMap.put(new PropertySimple(PROP_ALERT_DEFINITION_ID, def.getId())); alertDefinitionMap.put(new PropertySimple(PROP_ALERT_DEFINITION_NAME, def.getName())); alertDefinitionMap.put(new PropertySimple(PROP_RESOURCE_ID, def.getResource().getId())); results.add(alertDefinitionMap); } } private List<Integer> asIdList(String... ids) { List<Integer> ret = new ArrayList<Integer>(); for (String id : ids) { ret.add(Integer.parseInt(id)); } return ret; } private List<Integer> asIdList(Collection<AlertNotification> notifs) { ArrayList<Integer> ret = new ArrayList<Integer>(); for (AlertNotification n : notifs) { ret.add(n.getId()); } return ret; } public Set<String> getDomainPackagesNames() { return domainPackagesNames; } }