org.opennms.netmgt.dao.support.DefaultResourceDao.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.dao.support.DefaultResourceDao.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2007-2015 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2015 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.dao.support;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang.CharEncoding;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.netmgt.config.api.CollectdConfigFactory;
import org.opennms.netmgt.config.api.ResourceTypesDao;
import org.opennms.netmgt.config.collectd.Package;
import org.opennms.netmgt.dao.api.IpInterfaceDao;
import org.opennms.netmgt.dao.api.LocationMonitorDao;
import org.opennms.netmgt.dao.api.NodeDao;
import org.opennms.netmgt.dao.api.ResourceDao;
import org.opennms.netmgt.dao.api.ResourceStorageDao;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsLocationMonitor;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.OnmsResource;
import org.opennms.netmgt.model.OnmsResourceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.google.common.base.Throwables;
import com.google.common.collect.Maps;

/**
 * Retrieves and enumerates elements from the resource tree.
 *
 * This class is responsible for maintaining the list of
 * resource types and coordinating amongst these.
 *
 * All resource type specific logic should be contained in
 * the resource type implementations rather than this class.
 *
 * @author <a href="mailto:jesse@opennms.org">Jesse White</a>
 * @author <a href="mailto:seth@opennms.org">Seth Leger </a>
 * @author <a href="mailto:larry@opennms.org">Lawrence Karnowski </a>
 * @author <a href="mailto:dj@opennms.org">DJ Gregor</a>
 */
public class DefaultResourceDao implements ResourceDao, InitializingBean {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultResourceDao.class);

    private static final Pattern RESOURCE_ID_PATTERN = Pattern.compile("([^\\[]+)\\[([^\\]]*)\\](?:\\.|$)");

    /**
     * Largest depth at which we will find node related metrics:
     * [0] will catch node-level resources
     * [1] will catch interface-level resources
     * [2] will catch generic index type resources
     */
    public static final int MAXIMUM_NODE_METRIC_RESOURCE_DEPTH = 2;

    private ResourceStorageDao m_resourceStorageDao;
    private NodeDao m_nodeDao;
    private LocationMonitorDao m_locationMonitorDao;
    private IpInterfaceDao m_ipInterfaceDao;
    private CollectdConfigFactory m_collectdConfig;
    private ResourceTypesDao m_resourceTypesDao;
    private Date m_lastUpdatedResourceTypesConfig;

    private Map<String, OnmsResourceType> m_resourceTypes = Maps.newHashMap();
    private NodeResourceType m_nodeResourceType;

    /**
     * <p>Constructor for DefaultResourceDao.</p>
     */
    public DefaultResourceDao() {
    }

    public ResourceTypesDao getResourceTypesDao() {
        return m_resourceTypesDao;
    }

    public void setResourceTypesDao(ResourceTypesDao resourceTypesDao) {
        m_resourceTypesDao = Objects.requireNonNull(resourceTypesDao);
    }

    /**
     * <p>getNodeDao</p>
     *
     * @return a {@link org.opennms.netmgt.dao.api.NodeDao} object.
     */
    public NodeDao getNodeDao() {
        return m_nodeDao;
    }

    /**
     * <p>setNodeDao</p>
     *
     * @param nodeDao a {@link org.opennms.netmgt.dao.api.NodeDao} object.
     */
    public void setNodeDao(NodeDao nodeDao) {
        m_nodeDao = nodeDao;
    }

    /**
     * <p>getCollectdConfig</p>
     *
     * @return a {@link org.opennms.netmgt.config.CollectdConfigFactory} object.
     */
    public CollectdConfigFactory getCollectdConfig() {
        return m_collectdConfig;
    }

    /**
     * <p>setCollectdConfig</p>
     *
     * @param collectdConfig a {@link org.opennms.netmgt.config.CollectdConfigFactory} object.
     */
    public void setCollectdConfig(CollectdConfigFactory collectdConfig) {
        m_collectdConfig = collectdConfig;
    }

    /**
     * <p>getLocationMonitorDao</p>
     *
     * @return a {@link org.opennms.netmgt.dao.api.LocationMonitorDao} object.
     */
    public LocationMonitorDao getLocationMonitorDao() {
        return m_locationMonitorDao;
    }

    /**
     * <p>setLocationMonitorDao</p>
     *
     * @param locationMonitorDao a {@link org.opennms.netmgt.dao.api.LocationMonitorDao} object.
     */
    public void setLocationMonitorDao(LocationMonitorDao locationMonitorDao) {
        m_locationMonitorDao = locationMonitorDao;
    }

    public IpInterfaceDao getIpInterfaceDao() {
        return m_ipInterfaceDao;
    }

    public void setIpInterfaceDao(IpInterfaceDao ipInterfaceDao) {
        m_ipInterfaceDao = ipInterfaceDao;
    }

    public void setResourceStorageDao(ResourceStorageDao resourceStorageDao) {
        m_resourceStorageDao = resourceStorageDao;

        // The resource types depend on the resource resolver so
        // we reinitialize these when it changes
        if (m_resourceTypes.size() > 0) {
            initResourceTypes();
        }
    }

    public ResourceStorageDao getResourceStorageDao() {
        return m_resourceStorageDao;
    }

    /**
     * <p>afterPropertiesSet</p>
     */
    @Override
    public void afterPropertiesSet() {

        if (m_collectdConfig == null) {
            throw new IllegalStateException("collectdConfig property has not been set");
        }

        if (m_resourceTypesDao == null) {
            throw new IllegalStateException("resourceTypesDao property has not been set");
        }

        if (m_nodeDao == null) {
            throw new IllegalStateException("nodeDao property has not been set");
        }

        if (m_locationMonitorDao == null) {
            throw new IllegalStateException("locationMonitorDao property has not been set");
        }

        if (m_resourceStorageDao == null) {
            throw new IllegalStateException("resourceStorageDao property has not been set");
        }

        initResourceTypes();
    }

    private void initResourceTypes() {
        final Map<String, OnmsResourceType> resourceTypes = Maps.newLinkedHashMap();
        OnmsResourceType resourceType;

        resourceType = new NodeSnmpResourceType(m_resourceStorageDao);
        resourceTypes.put(resourceType.getName(), resourceType);

        resourceType = new InterfaceSnmpResourceType(m_resourceStorageDao);
        resourceTypes.put(resourceType.getName(), resourceType);

        resourceType = new ResponseTimeResourceType(m_resourceStorageDao, m_ipInterfaceDao);
        resourceTypes.put(resourceType.getName(), resourceType);

        resourceType = new DistributedStatusResourceType(m_resourceStorageDao, m_locationMonitorDao);
        resourceTypes.put(resourceType.getName(), resourceType);

        resourceTypes.putAll(
                GenericIndexResourceType.createTypes(m_resourceTypesDao.getResourceTypes(), m_resourceStorageDao));

        m_nodeResourceType = new NodeResourceType(this, m_nodeDao);
        resourceTypes.put(m_nodeResourceType.getName(), m_nodeResourceType);
        // Add 'nodeSource' as an alias to for the 'node' resource type to preserve backwards compatibility
        // See NMS-8404 for details
        resourceTypes.put("nodeSource", m_nodeResourceType);

        if (isDomainResourceTypeUsed()) {
            LOG.debug(
                    "One or more packages are configured with storeByIfAlias=true. Enabling the domain resource type.");
            resourceType = new DomainResourceType(this, m_resourceStorageDao);
            resourceTypes.put(resourceType.getName(), resourceType);
        } else {
            LOG.debug("No packages are configured with storeByIfAlias=true. Excluding the domain resource type.");
        }

        m_resourceTypes = resourceTypes;
        m_lastUpdatedResourceTypesConfig = m_resourceTypesDao.getLastUpdate();
    }

    /** {@inheritDoc} */
    @Override
    public Collection<OnmsResourceType> getResourceTypes() {
        if (isResourceTypesConfigChanged()) {
            LOG.debug("The resource type configuration has been changed, reloading resource types.");
            initResourceTypes();
        }
        return m_resourceTypes.values();
    }

    /** {@inheritDoc} */
    @Override
    @Transactional(readOnly = true)
    public List<OnmsResource> findTopLevelResources() {
        // Retrieve all top-level resources by passing null as the parent
        final List<OnmsResource> resources = m_resourceTypes.values().stream().distinct()
                .map(type -> type.getResourcesForParent(null)).flatMap(rs -> rs.stream())
                .filter(resource -> hasAnyChildResources(resource)).collect(Collectors.toList());
        return resources;
    }

    /**
     * Used to determine whether or not the given (parent) resource
     * has any child resources.
     */
    protected boolean hasAnyChildResources(OnmsResource resource) {
        // The order of the resource types matter here since we want to
        // check for the types are most likely occur first.
        return getResourceTypes().stream().anyMatch(t -> t.isResourceTypeOnParent(resource));
    }

    /**
     * Fetch a specific resource by string ID.
     *
     * @return Resource or null if resource cannot be found.
     * @throws IllegalArgumentException When the resource ID string does not match the expected regex pattern
     * @throws ObjectRetrievalFailureException If any exceptions are thrown while searching for the resource
     */
    @Override
    @Transactional(readOnly = true)
    public OnmsResource getResourceById(String id) {
        OnmsResource resource = null;

        Matcher m = RESOURCE_ID_PATTERN.matcher(id);
        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            String resourceTypeName = DefaultResourceDao.decode(m.group(1));
            String resourceName = DefaultResourceDao.decode(m.group(2));

            try {
                resource = getChildResource(resource, resourceTypeName, resourceName);
            } catch (Throwable e) {
                LOG.warn("Could not get resource for resource ID \"{}\"", id, e);
                return null;
            }

            m.appendReplacement(sb, "");
        }

        m.appendTail(sb);

        if (sb.length() > 0) {
            LOG.warn("Resource ID '{}' does not match pattern '{}' at '{}'", id, RESOURCE_ID_PATTERN, sb);
            return null;
        } else {
            return resource;
        }
    }

    /**
     * Creates a resource for the given node using the most
     * appropriate type.
     */
    @Override
    public OnmsResource getResourceForNode(OnmsNode node) {
        Assert.notNull(node, "node argument must not be null");
        return m_nodeResourceType.createResourceForNode(node);
    }

    /**
     * @return OnmsResource for the <code>distributedStatus</code> resource on the interface or 
     * null if the <code>distributedStatus</code> resource cannot be found for the given IP interface.
     */
    @Override
    public OnmsResource getResourceForIpInterface(OnmsIpInterface ipInterface, OnmsLocationMonitor locMon) {
        Assert.notNull(ipInterface, "ipInterface argument must not be null");
        Assert.notNull(locMon, "locMon argument must not be null");
        Assert.notNull(ipInterface.getNode(), "getNode() on ipInterface must not return null");

        final String ipAddress = InetAddressUtils.str(ipInterface.getIpAddress());
        final OnmsResource nodeResource = getResourceForNode(ipInterface.getNode());
        return getChildResource(nodeResource, DistributedStatusResourceType.TYPE_NAME,
                DistributedStatusResourceType.getResourceName(locMon.getId(), ipAddress));
    }

    @Override
    public boolean deleteResourceById(final String resourceId) {
        final OnmsResource resource = this.getResourceById(resourceId);
        if (resource == null) {
            return false;
        }

        return deleteResource(resource, true);
    }

    public boolean deleteResource(final OnmsResource resource, boolean recursive) {
        boolean result = false;

        if (recursive) {
            for (final OnmsResource childResource : resource.getChildResources()) {
                result = deleteResource(childResource, recursive) || result;
            }
        }

        result = m_resourceStorageDao.delete(resource.getPath()) || result;

        return result;
    }

    /**
     * <p>getChildResource</p>
     *
     * @param parentResource a {@link org.opennms.netmgt.model.OnmsResource} object.
     * @param resourceType a {@link java.lang.String} object.
     * @param resource a {@link java.lang.String} object.
     * @return a {@link org.opennms.netmgt.model.OnmsResource} object.
     */
    protected OnmsResource getChildResource(OnmsResource parentResource, String resourceType, String resource) {
        final OnmsResourceType targetType = m_resourceTypes.get(resourceType);
        if (targetType == null) {
            throw new ObjectRetrievalFailureException(OnmsResource.class, resourceType + "/" + resource,
                    "Unsupported resource type: " + resourceType, null);
        }

        final OnmsResource childResource = targetType.getChildByName(parentResource, resource);
        if (childResource != null) {
            LOG.debug("getChildResource: returning resource {}", childResource);
            return childResource;
        }

        throw new ObjectRetrievalFailureException(OnmsResource.class, resourceType + "/" + resource,
                "Could not find child resource '" + resource + "' with resource type '" + resourceType
                        + "' on resource '" + resource + "'",
                null);
    }

    private boolean isResourceTypesConfigChanged() {
        Date current = m_resourceTypesDao.getLastUpdate();
        if (current.after(m_lastUpdatedResourceTypesConfig)) {
            m_lastUpdatedResourceTypesConfig = current;
            return true;
        }
        return false;
    }

    /**
     * Encapsulate the deprecated decode method to fix it in one place.
     *
     * @param string
     *            string to be decoded
     * @return decoded string
     */
    public static String decode(String string) {
        try {
            return URLDecoder.decode(string, CharEncoding.UTF_8);
        } catch (UnsupportedEncodingException e) {
            // UTF-8 should *never* throw this
            throw Throwables.propagate(e);
        }
    }

    /**
     * For performance reasons, we only enable the {@link DomainResourceType} if
     * one or more packages use it. Here we iterator over all of defined packages,
     * return true if a package uses the domain types, and false otherwise.
     */
    private boolean isDomainResourceTypeUsed() {
        for (Package pkg : m_collectdConfig.getCollectdConfig().getPackages()) {
            if ("true".equalsIgnoreCase(pkg.getStoreByIfAlias())) {
                return true;
            }
        }
        return false;
    }
}