org.opennms.features.topology.app.internal.IpLikeSearchProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.features.topology.app.internal.IpLikeSearchProvider.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 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.features.topology.app.internal;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.opennms.core.criteria.Criteria;
import org.opennms.core.criteria.CriteriaBuilder;
import org.opennms.features.topology.api.GraphContainer;
import org.opennms.features.topology.api.OperationContext;
import org.opennms.features.topology.api.support.VertexHopGraphProvider;
import org.opennms.features.topology.api.topo.AbstractSearchProvider;
import org.opennms.features.topology.api.topo.CollapsibleCriteria;
import org.opennms.features.topology.api.topo.SearchProvider;
import org.opennms.features.topology.api.topo.SearchQuery;
import org.opennms.features.topology.api.topo.SearchResult;
import org.opennms.features.topology.api.topo.VertexRef;
import org.opennms.features.topology.app.internal.support.IpLikeHopCriteria;
import org.opennms.features.topology.app.internal.support.IpLikeHopCriteriaFactory;
import org.opennms.netmgt.dao.api.IpInterfaceDao;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of the <SearchProvider> API that allows the user to specify
 * an IPLIKE query.  The query itself is returned in the <SearchResult> set as
 * a collapsible selection as well as the "distinct" list of IPs from the DB
 * that are also collapsible since multiple nodes may have the same IP.  The
 * <IpLikeHopCriteria> supports adding all the nodes having the selected IP.
 * 
 * FIXME: Improve the label to support showing how many vertices are supported
 * by each <SearchResult>
 * 
 * @author <a href=mailto:david@opennms.org>David Hustace</a>
 *
 */
public class IpLikeSearchProvider extends AbstractSearchProvider implements SearchProvider {

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

    private IpInterfaceDao m_ipInterfaceDao;
    private IpLikeHopCriteriaFactory m_ipLikeHopFactory;

    private static final Pattern m_iplikePattern = Pattern
            .compile("^[0-9?,\\-*]*\\.[0-9?,\\-*]*\\.[0-9?,\\-*]*\\.[0-9?,\\-*]*$");

    public IpLikeSearchProvider(IpInterfaceDao ipInterfaceDao) {
        m_ipInterfaceDao = ipInterfaceDao;
        m_ipLikeHopFactory = new IpLikeHopCriteriaFactory(m_ipInterfaceDao);
    }

    @Override
    public String getSearchProviderNamespace() {
        return IpLikeHopCriteria.NAMESPACE;
    }

    @Override
    public boolean contributesTo(String namespace) {
        return "nodes".equals(namespace);
    }

    /**
     * This method processes the <SearchQuery> that the user has typed and returns a <SearchResult> list
     * of matching IP addresses as well as the query string itself, which is collapsible, to act
     * as a subnet container.
     * 
     */
    @Override
    public List<SearchResult> query(SearchQuery searchQuery, GraphContainer graphContainer) {
        LOG.info("SearchProvider->query: called with search query: '{}'", searchQuery);

        List<SearchResult> results = new ArrayList<SearchResult>();

        String queryString = searchQuery.getQueryString();

        if (!isIpLikeQuery(queryString)) {
            LOG.debug("SearchProvider->query: query not IPLIKE compatible.");
            return results;
        }

        CriteriaBuilder bldr = new CriteriaBuilder(OnmsIpInterface.class);

        bldr.iplike("ipAddress", queryString).orderBy("ipAddress", true);
        Criteria dbQueryCriteria = bldr.toCriteria();
        List<OnmsIpInterface> ips;

        //Here to catch query exceptions and avoid unnecessary exception messages in the Vaadin UI
        //Mainly do to not having yet found the perfect iplike regex that supports IPLIKE syntax 
        //for both IPv4 and IPv6 IPLIKE expressions in the isIpLikeQuery method.  The current Pattern
        //test currently fails not catching octets ending in '-' such as '10.7-.*.*' and only supports
        //IPv4 addresses.  This just lets us fail to the underlying IPLIKE stored procedure.  The IPLIKE
        //Utils class might be a good place to have a static method that validates the query string
        //since it seems to do something very similar in its matches method.
        try {
            ips = m_ipInterfaceDao.findMatching(dbQueryCriteria);
            if (ips.size() == 0) {
                return results;
            } else {
                if (isIpLikeQuery(queryString)) {
                    LOG.debug("SearchProvider->query: adding IPLIKE search spec '{}' to the search results.",
                            queryString);
                    SearchResult searchResult = new SearchResult(getSearchProviderNamespace(), queryString,
                            queryString, queryString);
                    searchResult.setCollapsed(false);
                    searchResult.setCollapsible(true);
                    results.add(searchResult);
                }
            }

            Set<String> ipAddrs = new HashSet<String>();

            LOG.info("SearchProvider->query: creating IP address set.");
            for (OnmsIpInterface ip : ips) {
                String hostAddress = ip.getIpAddress().getHostAddress();
                LOG.debug("SearchProvider->query: adding '{}' to set of IPs.", hostAddress);
                ipAddrs.add(hostAddress);
            }

            LOG.info("SearchProvider->query: building search result from set of IPs.");
            IPLOOP: for (String ip : ipAddrs) {

                if (findCriterion(ip, graphContainer) != null) {
                    continue IPLOOP;

                } else {
                    results.add(createSearchResult(ip, queryString));

                }
            }
            LOG.info("SearchProvider->query: found: '{}' IP interfaces.", ips.size());

        } catch (Exception e) {
            LOG.error("SearchProvider-query: caught exception during iplike query: {}", e);

        }

        LOG.info("SearchProvider->query: built search result with {} results.", results.size());

        return results;
    }

    private SearchResult createSearchResult(String ip, String queryString) {
        SearchResult result = new SearchResult(getSearchProviderNamespace(), ip, ip, queryString);
        result.setCollapsible(true);
        return result;
    }

    /**
     * Validate if the query string has any chance of being an IPLIKE query.
     * 
     * FIXME: validates iplike strings that have an octet ending in '-' such as:
     * 10.7-.*.*
     * 
     * @param queryString
     * @return true if the string contains a '*' or '-' or ',' ".
     */
    private boolean isIpLikeQuery(String queryString) {

        //        Matcher iplikeMatcher = m_iplikePattern.matcher(queryString);
        //        return iplikeMatcher.matches();

        boolean validity = false;

        int ipv4delimCnt = StringUtils.countMatches(queryString, ".");
        int ipv6delimCnt = StringUtils.countMatches(queryString, ":");

        if ((ipv4delimCnt == 3 || ipv6delimCnt == 7) && !StringUtils.endsWith(queryString, "-")) {
            validity = true;
        } else {
            validity = false;
        }

        return validity;

    }

    @Override
    public boolean supportsPrefix(String searchPrefix) {
        return supportsPrefix("iplike=", searchPrefix);
    }

    //FIXME so that the focus button works.???
    @Override
    public Set<VertexRef> getVertexRefsBy(SearchResult searchResult, GraphContainer container) {

        LOG.debug("SearchProvider->getVertexRefsBy: called with search result: '{}'", searchResult);
        org.opennms.features.topology.api.topo.Criteria criterion = findCriterion(searchResult.getId(), container);

        Set<VertexRef> vertices = ((IpLikeHopCriteria) criterion).getVertices();
        LOG.debug("SearchProvider->getVertexRefsBy: found '{}' vertices.", vertices.size());

        return vertices;

    }

    @Override
    public void onCenterSearchResult(SearchResult searchResult, GraphContainer graphContainer) {
        LOG.debug("SearchProvider->onCenterSearchResult: called with search result: '{}'", searchResult);
        super.onCenterSearchResult(searchResult, graphContainer);
    }

    @Override
    public void onFocusSearchResult(SearchResult searchResult, OperationContext operationContext) {
        LOG.debug("SearchProvider->onFocusSearchResult: called with search result: '{}'", searchResult);
        super.onFocusSearchResult(searchResult, operationContext);

    }

    @Override
    public void onDefocusSearchResult(SearchResult searchResult, OperationContext operationContext) {
        LOG.debug("SearchProvider->onDefocusSearchResult: called with search result: '{}'", searchResult);
        super.onDefocusSearchResult(searchResult, operationContext);

    }

    /**
     * Creates a criteria that provides <VertexRefs> matching the IPLIKE query from the users query
     * stored in the <SearchResult> that was created by this class during the query method.  The SearchResult 
     * and the Criterion use the OnmsIpinterfaceId as the ID for dereferencing in the container.
     */
    @Override
    public void addVertexHopCriteria(SearchResult searchResult, GraphContainer container) {
        LOG.debug("SearchProvider->addVertexHopCriteria: called with search result: '{}'", searchResult);

        IpLikeHopCriteria criterion = m_ipLikeHopFactory.createCriteria(searchResult.getLabel());
        String id = searchResult.getId();
        criterion.setId(id);
        container.addCriteria(criterion);

        LOG.debug("SearchProvider->addVertexHop: adding hop criteria {}.", criterion);

        logCriteriaInContainer(container);

    }

    private void logCriteriaInContainer(GraphContainer container) {
        org.opennms.features.topology.api.topo.Criteria[] criteria = container.getCriteria();
        LOG.debug("SearchProvider->addVertexHopCriteria: there are now {} criteria in the GraphContainer.",
                criteria.length);
        for (org.opennms.features.topology.api.topo.Criteria crit : criteria) {
            LOG.debug("SearchProvider->addVertexHopCriteria: criterion: '{}' is in the GraphContainer.", crit);
        }
    }

    @Override
    public void removeVertexHopCriteria(SearchResult searchResult, GraphContainer container) {
        LOG.debug("SearchProvider->removeVertexHopCriteria: called with search result: '{}'", searchResult);
        org.opennms.features.topology.api.topo.Criteria criterian = findCriterion(searchResult.getId(), container);

        if (criterian != null) {
            container.removeCriteria(criterian);
        }
        logCriteriaInContainer(container);
    }

    @Override
    public void onToggleCollapse(SearchResult searchResult, GraphContainer graphContainer) {
        LOG.debug("SearchProvider->onToggleCollapse: called with search result: '{}'", searchResult);

        CollapsibleCriteria criteria = getMatchingCriteriaById(graphContainer, searchResult.getId());
        if (criteria != null) {
            criteria.setCollapsed(!criteria.isCollapsed());
            graphContainer.redoLayout();
        }

    }

    private org.opennms.features.topology.api.topo.Criteria findCriterion(String resultId,
            GraphContainer container) {

        org.opennms.features.topology.api.topo.Criteria[] criteria = container.getCriteria();
        for (org.opennms.features.topology.api.topo.Criteria criterion : criteria) {
            if (criterion instanceof IpLikeHopCriteria) {

                String id = ((IpLikeHopCriteria) criterion).getId();

                if (id.equals(resultId)) {
                    return criterion;
                }
            }
        }
        return null;
    }

    private static CollapsibleCriteria getMatchingCriteriaById(GraphContainer graphContainer, String id) {
        CollapsibleCriteria[] criteria = VertexHopGraphProvider.getCollapsibleCriteriaForContainer(graphContainer);
        for (CollapsibleCriteria criterium : criteria) {
            if (criterium.getId().equals(id)) {
                return criterium;
            }
        }
        return null;
    }

}