/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-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.importer.operations; import java.util.ArrayList; 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.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.opennms.core.concurrent.LogPreservingThreadFactory; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.dao.OnmsDao; import org.opennms.netmgt.eventd.EventIpcManager; import org.opennms.netmgt.xml.event.Event; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; /** * This nodes job is to tracks nodes that need to be deleted, added, or changed * * @author david * @version $Id: $ */ public class ImportOperationsManager { private List<ImportOperation> m_inserts = new LinkedList<ImportOperation>(); private List<ImportOperation> m_updates = new LinkedList<ImportOperation>(); private Map<String, Integer> m_foreignIdToNodeMap; private ImportOperationFactory m_operationFactory; private ImportStatistics m_stats = new DefaultImportStatistics(); private EventIpcManager m_eventMgr; private int m_scanThreads = 50; private int m_writeThreads = 4; private String m_foreignSource; /** * <p>Constructor for ImportOperationsManager.</p> * * @param foreignIdToNodeMap a {@link java.util.Map} object. * @param operationFactory a {@link org.opennms.netmgt.importer.operations.ImportOperationFactory} object. */ public ImportOperationsManager(Map<String, Integer> foreignIdToNodeMap, ImportOperationFactory operationFactory) { m_foreignIdToNodeMap = new HashMap<String, Integer>(foreignIdToNodeMap); m_operationFactory = operationFactory; } /** * <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.importer.operations.SaveOrUpdateOperation} object. */ public SaveOrUpdateOperation foundNode(String foreignId, String nodeLabel, String building, String city) { if (nodeExists(foreignId)) { return updateNode(foreignId, nodeLabel, building, city); } else { return insertNode(foreignId, nodeLabel, building, city); } } private boolean nodeExists(String foreignId) { return m_foreignIdToNodeMap.containsKey(foreignId); } private SaveOrUpdateOperation insertNode(String foreignId, String nodeLabel, String building, String city) { InsertOperation insertOperation = m_operationFactory.createInsertOperation(getForeignSource(), foreignId, nodeLabel, building, city); m_inserts.add(insertOperation); return insertOperation; } private SaveOrUpdateOperation updateNode(String foreignId, String nodeLabel, String building, String city) { Integer nodeId = processForeignId(foreignId); UpdateOperation updateOperation = m_operationFactory.createUpdateOperation(nodeId, getForeignSource(), foreignId, nodeLabel, building, city); 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(); } class DeleteIterator implements Iterator<ImportOperation> { private 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(); Integer nodeId = entry.getValue(); String foreignId = entry.getKey(); return m_operationFactory.createDeleteOperation(nodeId, m_foreignSource, foreignId); } public void remove() { m_foreignIdIterator.remove(); } } class OperationIterator implements Iterator<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(); } } /** * <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 (InterruptedException e) { log().error(msg, e); } } /** * <p>persistOperations</p> * * @param template a {@link org.springframework.transaction.support.TransactionTemplate} object. * @param dao a {@link org.opennms.netmgt.dao.OnmsDao} object. */ public void persistOperations(TransactionTemplate template, OnmsDao<?, ?> dao) { m_stats.beginProcessingOps(); m_stats.setDeleteCount(getDeleteCount()); m_stats.setInsertCount(getInsertCount()); m_stats.setUpdateCount(getUpdateCount()); ExecutorService pool = Executors.newFixedThreadPool(m_writeThreads, new LogPreservingThreadFactory(getClass().getSimpleName() + ".persistOperations", m_writeThreads, false)); preprocessOperations(template, dao, new OperationIterator(), pool); shutdownAndWaitForCompletion(pool, "persister interrupted!"); m_stats.finishProcessingOps(); } private void preprocessOperations(final TransactionTemplate template, final OnmsDao<?, ?> dao, OperationIterator iterator, final ExecutorService dbPool) { m_stats.beginPreprocessingOps(); ExecutorService pool = Executors.newFixedThreadPool(m_scanThreads, new LogPreservingThreadFactory(getClass().getSimpleName() + ".preprocessOperations", m_scanThreads, false)); for (Iterator<ImportOperation> it = iterator; it.hasNext();) { final ImportOperation oper = it.next(); Runnable r = new Runnable() { public void run() { preprocessOperation(oper, template, dao, dbPool); } }; pool.execute(r); } shutdownAndWaitForCompletion(pool, "preprocessor interrupted!"); m_stats.finishPreprocessingOps(); } /** * <p>preprocessOperation</p> * * @param oper a {@link org.opennms.netmgt.importer.operations.ImportOperation} object. * @param template a {@link org.springframework.transaction.support.TransactionTemplate} object. * @param dao a {@link org.opennms.netmgt.dao.OnmsDao} object. * @param dbPool a {@link java.util.concurrent.ExecutorService} object. */ protected void preprocessOperation(final ImportOperation oper, final TransactionTemplate template, final OnmsDao<?, ?> dao, final ExecutorService dbPool) { m_stats.beginPreprocessing(oper); log().info("Preprocess: "+oper); oper.gatherAdditionalData(); Runnable r = new Runnable() { public void run() { persistOperation(oper, template, dao); } }; dbPool.execute(r); m_stats.finishPreprocessing(oper); } /** * <p>persistOperation</p> * * @param oper a {@link org.opennms.netmgt.importer.operations.ImportOperation} object. * @param template a {@link org.springframework.transaction.support.TransactionTemplate} object. * @param dao a {@link org.opennms.netmgt.dao.OnmsDao} object. */ protected void persistOperation(final ImportOperation oper, TransactionTemplate template, final OnmsDao<?, ?> dao) { m_stats.beginPersisting(oper); log().info("Persist: "+oper); List<Event> events = persistToDatabase(oper, template); m_stats.finishPersisting(oper); if (m_eventMgr != null && events != null) { m_stats.beginSendingEvents(oper, events); log().info("Send Events: "+oper); // now send the events for the update for (Iterator<Event> eventIt = events.iterator(); eventIt.hasNext();) { Event event = eventIt.next(); m_eventMgr.sendNow(event); } m_stats.finishSendingEvents(oper, events); } log().info("Clear cache: "+oper); // clear the cache to we don't use up all the memory dao.clear(); } /** * Persist the import operation changes to the database. * * @param oper changes to persist * @param template transaction template in which to perform the persist operation * @return list of events */ private List<Event> persistToDatabase(final ImportOperation oper, TransactionTemplate template) { List<Event> events = template.execute(new TransactionCallback<List<Event>>() { public List<Event> doInTransaction(TransactionStatus status) { List<Event> result = oper.persist(); return result; } }); return events; } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } /** * <p>setScanThreads</p> * * @param scanThreads a int. */ public void setScanThreads(int scanThreads) { m_scanThreads = scanThreads; } /** * <p>setWriteThreads</p> * * @param writeThreads a int. */ public void setWriteThreads(int writeThreads) { m_writeThreads = writeThreads; } /** * <p>getEventMgr</p> * * @return a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public EventIpcManager getEventMgr() { return m_eventMgr; } /** * <p>setEventMgr</p> * * @param eventMgr a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public void setEventMgr(EventIpcManager eventMgr) { m_eventMgr = eventMgr; } /** * <p>getStats</p> * * @return a {@link org.opennms.netmgt.importer.operations.ImportStatistics} object. */ public ImportStatistics getStats() { return m_stats; } /** * <p>setStats</p> * * @param stats a {@link org.opennms.netmgt.importer.operations.ImportStatistics} object. */ public void setStats(ImportStatistics stats) { m_stats = stats; } /** * <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; } }