/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2008-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 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 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 General Public License for more details. * * You should have received a copy of the GNU 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.provision.service.operations; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.provision.persist.requisition.Requisition; import org.opennms.netmgt.provision.service.RequisitionAccountant; import org.opennms.netmgt.provision.service.ProvisionService; /** * This class tracks nodes that need to be deleted, inserted, or updated during * provisioning import operations. * * @author david */ public class ImportOperationsManager { public static final class NullUpdateOperation extends UpdateOperation { public NullUpdateOperation(final Integer nodeId, final String foreignSource, final String foreignId, final String nodeLabel, final String building, final String city, final ProvisionService provisionService) { super(nodeId, foreignSource, foreignId, nodeLabel, building, city, provisionService); } @Override protected void doPersist() { LogUtils.debugf(this, "Skipping persist for node %s: rescanExisting is false", getNode()); } } /** * TODO: Seth 2012-03-08: These lists may consume a lot of RAM for large provisioning * groups. We may need to figure out how to use flyweight objects instead of heavier * {@link OnmsNode} objects in these lists. Our goal is to handle 50,000+ nodes per * import operation. */ private final List<ImportOperation> m_inserts = new LinkedList<ImportOperation>(); private final List<ImportOperation> m_updates = new LinkedList<ImportOperation>(); private final ProvisionService m_provisionService; private final Map<String, Integer> m_foreignIdToNodeMap; private Boolean m_rescanExisting; private String m_foreignSource; /** * <p>Constructor for ImportOperationsManager.</p> * * @param foreignIdToNodeMap a {@link java.util.Map} object. * @param provisionService a {@link org.opennms.netmgt.provision.service.ProvisionService} object. * @param rescanExisting TODO */ public ImportOperationsManager(Map<String, Integer> foreignIdToNodeMap, ProvisionService provisionService, final Boolean rescanExisting) { m_provisionService = provisionService; m_foreignIdToNodeMap = new HashMap<String, Integer>(foreignIdToNodeMap); m_rescanExisting = rescanExisting; } /** * <p>foundNode</p> * * @param foreignId a {@link java.lang.String} object. * @param nodeLabel a {@link java.lang.String} object. * @param building a {@link java.lang.String} object. * @param city a {@link java.lang.String} object. * @return a {@link org.opennms.netmgt.provision.service.operations.SaveOrUpdateOperation} object. */ public SaveOrUpdateOperation foundNode(String foreignId, String nodeLabel, String building, String city) { SaveOrUpdateOperation ret; if (nodeExists(foreignId)) { ret = updateNode(foreignId, nodeLabel, building, city); } else { ret = insertNode(foreignId, nodeLabel, building, city); } return ret; } private boolean nodeExists(String foreignId) { return m_foreignIdToNodeMap.containsKey(foreignId); } private SaveOrUpdateOperation insertNode(final String foreignId, final String nodeLabel, final String building, final String city) { SaveOrUpdateOperation insertOperation = new InsertOperation(getForeignSource(), foreignId, nodeLabel, building, city, m_provisionService); m_inserts.add(insertOperation); return insertOperation; } private SaveOrUpdateOperation updateNode(final String foreignId, final String nodeLabel, final String building, final String city) { final Integer nodeId = processForeignId(foreignId); final UpdateOperation updateOperation; if (m_rescanExisting) { updateOperation = new UpdateOperation(nodeId, getForeignSource(), foreignId, nodeLabel, building, city, m_provisionService); } else { updateOperation = new NullUpdateOperation(nodeId, getForeignSource(), foreignId, nodeLabel, building, city, m_provisionService); } m_updates.add(updateOperation); return updateOperation; } /** * Return NodeId and remove it from the Map so we know which nodes have been operated on thereby * tracking nodes to be deleted. * @param foreignId * @return a nodeId */ private Integer processForeignId(String foreignId) { return m_foreignIdToNodeMap.remove(foreignId); } /** * <p>getOperationCount</p> * * @return a int. */ public int getOperationCount() { return m_inserts.size() + m_updates.size() + m_foreignIdToNodeMap.size(); } /** * <p>getInsertCount</p> * * @return a int. */ public int getInsertCount() { return m_inserts.size(); } /** * <p>getUpdateCount</p> * * @return a int. */ public int getUpdateCount() { return m_updates.size(); } /** * <p>getDeleteCount</p> * * @return a int. */ public int getDeleteCount() { return m_foreignIdToNodeMap.size(); } private class DeleteIterator implements Iterator<ImportOperation> { private final Iterator<Entry<String, Integer>> m_foreignIdIterator = m_foreignIdToNodeMap.entrySet().iterator(); public boolean hasNext() { return m_foreignIdIterator.hasNext(); } public ImportOperation next() { Entry<String, Integer> entry = m_foreignIdIterator.next(); return new DeleteOperation(entry.getValue(), getForeignSource(), entry.getKey(), m_provisionService); } public void remove() { m_foreignIdIterator.remove(); } } private class OperationIterator implements Iterator<ImportOperation>, Enumeration<ImportOperation> { Iterator<Iterator<ImportOperation>> m_iterIter; Iterator<ImportOperation> m_currentIter; OperationIterator() { List<Iterator<ImportOperation>> iters = new ArrayList<Iterator<ImportOperation>>(3); iters.add(new DeleteIterator()); iters.add(m_updates.iterator()); iters.add(m_inserts.iterator()); m_iterIter = iters.iterator(); } public boolean hasNext() { while((m_currentIter == null || !m_currentIter.hasNext()) && m_iterIter.hasNext()) { m_currentIter = m_iterIter.next(); m_iterIter.remove(); } return (m_currentIter == null ? false: m_currentIter.hasNext()); } public ImportOperation next() { return m_currentIter.next(); } public void remove() { m_currentIter.remove(); } public boolean hasMoreElements() { return hasNext(); } public ImportOperation nextElement() { return next(); } } /** * <p>shutdownAndWaitForCompletion</p> * * @param executorService a {@link java.util.concurrent.ExecutorService} object. * @param msg a {@link java.lang.String} object. */ public void shutdownAndWaitForCompletion(ExecutorService executorService, String msg) { executorService.shutdown(); try { while (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { // loop util the await returns false } } catch (final InterruptedException e) { log().error(msg, e); Thread.currentThread().interrupt(); } } /** * <p>getOperations</p> * * @return a {@link java.util.Collection} object. */ public Collection<ImportOperation> getOperations() { return Collections.list(new OperationIterator()); } @SuppressWarnings("unused") private Runnable sequence(final Executor pool, final Runnable a, final Runnable b) { return new Runnable() { public void run() { a.run(); pool.execute(b); } }; } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } /** * <p>setForeignSource</p> * * @param foreignSource a {@link java.lang.String} object. */ public void setForeignSource(String foreignSource) { m_foreignSource = foreignSource; } /** * <p>getForeignSource</p> * * @return a {@link java.lang.String} object. */ public String getForeignSource() { return m_foreignSource; } public Boolean getRescanExisting() { return m_rescanExisting; } /** * <p>auditNodes</p> * * @param requisition a {@link org.opennms.netmgt.provision.persist.requisition.Requisition} object. */ public void auditNodes(Requisition requisition) { requisition.visit(new RequisitionAccountant(this)); } @SuppressWarnings("unused") private Runnable persister(final ImportOperation oper) { Runnable r = new Runnable() { public void run() { oper.persist(); } }; return r; } @SuppressWarnings("unused") private Runnable scanner(final ImportOperation oper) { return new Runnable() { public void run() { log().info("Preprocess: "+oper); oper.scan(); } }; } }