org.opennms.netmgt.discovery.actors.RangeChunker.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.discovery.actors.RangeChunker.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2015-2016 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2016 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.discovery.actors;

import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.opennms.core.network.IPAddress;
import org.opennms.netmgt.config.DiscoveryConfigFactory;
import org.opennms.netmgt.config.discovery.DiscoveryConfiguration;
import org.opennms.netmgt.dao.api.MonitoringLocationDao;
import org.opennms.netmgt.discovery.IpAddressFilter;
import org.opennms.netmgt.discovery.messages.DiscoveryJob;
import org.opennms.netmgt.model.discovery.IPPollRange;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

/**
 * <p>This class generates a list of {@link DiscoveryJob} instances that
 * are based on a "chunk" of a number of IP addresses that should be
 * polled as part of each job.</p>
 *
 * <ul>
 * <li>Input: {@link DiscoveryConfiguration}</li>
 * <li>Input: {@link List<DiscoveryJob>}</li>
 * </ul>
 */
public class RangeChunker {

    private static class ForeignSourceLocationKey {
        private final String m_location;
        private final String m_foreignSource;

        public ForeignSourceLocationKey(String foreignSource, String location) {
            m_location = location;
            m_foreignSource = foreignSource;
        }

        public String getForeignSource() {
            return m_foreignSource;
        }

        public String getLocation() {
            return m_location;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (obj == this) {
                return true;
            }
            if (obj.getClass() != getClass()) {
                return false;
            }
            ForeignSourceLocationKey key = (ForeignSourceLocationKey) obj;
            return new EqualsBuilder().append(m_foreignSource, key.getForeignSource())
                    .append(m_location, key.getLocation()).isEquals();
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder().append(m_foreignSource).append(m_location).toHashCode();
        }
    }

    private IpAddressFilter m_ipAddressFilter;

    public void setIpAddressFilter(IpAddressFilter ipAddressFilter) {
        m_ipAddressFilter = ipAddressFilter;
    }

    public List<DiscoveryJob> chunk(final DiscoveryConfiguration config) {

        final int chunkSize = (config.getChunkSize() > 0) ? config.getChunkSize()
                : DiscoveryConfigFactory.DEFAULT_CHUNK_SIZE;
        final double packetsPerSecond = (config.getPacketsPerSecond() > 0.0) ? config.getPacketsPerSecond()
                : DiscoveryConfigFactory.DEFAULT_PACKETS_PER_SECOND;

        // If the foreign source for the discovery config is not set than use 
        // the default foreign source
        final String foreignSourceFromConfig = (config.getForeignSource() == null
                || "".equals(config.getForeignSource().trim())) ? "default" : config.getForeignSource().trim();

        // If the monitoring location for the discovery config is not set than use 
        // the default localhost location
        final String locationFromConfig = (config.getLocation() == null || "".equals(config.getLocation().trim()))
                ? MonitoringLocationDao.DEFAULT_MONITORING_LOCATION_ID
                : config.getLocation().trim();

        final DiscoveryConfigFactory configFactory = new DiscoveryConfigFactory(config);

        final AtomicReference<IPPollRange> previousRange = new AtomicReference<>();

        return StreamSupport.stream(configFactory.getConfiguredAddresses().spliterator(), false).filter(address -> {
            // If there is no IP address filter set or the filter matches
            return m_ipAddressFilter == null || m_ipAddressFilter.matches(address.getAddress());
        })
                // TODO: We could optimize this further by not unrolling IPPollRanges into individual
                // IPPollAddresses during the mapping.
                .map(address -> {
                    // Create a singleton IPPollRange
                    return new IPPollRange(
                            // Make sure that foreignSource is not null so that we can partition on the value
                            address.getForeignSource() == null ? foreignSourceFromConfig
                                    : address.getForeignSource(),
                            // Make sure that location is not null so that we can partition on the value
                            address.getLocation() == null ? locationFromConfig : address.getLocation(),
                            address.getAddress(), address.getAddress(), address.getTimeout(), address.getRetries());
                }).collect(Collectors.groupingBy(range -> {
                    // Create a Map<ForeignSourceLocationKey,List<IPPollRange>>
                    return new ForeignSourceLocationKey(
                            // Make sure that foreignSource is not null so that we can partition on the value
                            range.getForeignSource() == null ? foreignSourceFromConfig : range.getForeignSource(),
                            // Make sure that location is not null so that we can partition on the value
                            range.getLocation() == null ? locationFromConfig : range.getLocation());
                })).entrySet().stream()
                // Flat map one list of IPPollRanges to many chunked DiscoveryJobs
                .flatMap(entry -> {
                    // Partition the list of address values
                    return Lists.partition(entry.getValue(), chunkSize).stream()
                            // Map each partition value to a separate DiscoveryJob
                            .map(ranges -> {
                                DiscoveryJob retval = new DiscoveryJob(ranges.stream().map(address -> {
                                    // If this address is consecutive with the previous range,
                                    // then just extend the range to cover this address too
                                    if (isConsecutive(previousRange.get(), address)) {
                                        previousRange.get().getAddressRange().incrementEnd();
                                        return null;
                                    }
                                    previousRange.set(address);
                                    return address;
                                })
                                        // Filter out all of the consecutive values that we nulled out
                                        .filter(Objects::nonNull)
                                        // Convert back into a list of ranges
                                        .collect(Collectors.toList()), entry.getKey().getForeignSource(),
                                        entry.getKey().getLocation(), packetsPerSecond);
                                // Reset the previousRange value
                                previousRange.set(null);
                                return retval;
                            })
                            // Collect the DiscoveryJobs
                            .collect(Collectors.toList()).stream();
                }).collect(Collectors.toList());
    }

    private static boolean isConsecutive(IPPollRange range, IPPollRange address) {
        Preconditions.checkState(BigInteger.ONE.equals(address.getAddressRange().size()));
        return range != null
                && new IPAddress(range.getAddressRange().getEnd())
                        .isPredecessorOf(new IPAddress(address.getAddressRange().getEnd()))
                && range.getForeignSource().equals(address.getForeignSource())
                && range.getLocation().equals(address.getLocation()) && range.getRetries() == address.getRetries()
                && range.getTimeout() == address.getTimeout();
    }
}