/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 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.snmp; import java.io.IOException; import java.net.InetAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.opennms.core.utils.ThreadCategory; public abstract class SnmpWalker { protected static abstract class WalkerPduBuilder extends PduBuilder { protected WalkerPduBuilder(int maxVarsPerPdu) { super(maxVarsPerPdu); } abstract public void reset(); } private final String m_name; private final CollectionTracker m_tracker; private final CountDownLatch m_signal; private InetAddress m_address; private WalkerPduBuilder m_pduBuilder; private ResponseProcessor m_responseProcessor; private final int m_maxVarsPerPdu; private boolean m_error = false; private String m_errorMessage = ""; private Throwable m_errorThrowable = null; protected SnmpWalker(InetAddress address, String name, int maxVarsPerPdu, int maxRepititions, CollectionTracker tracker) { m_address = address; m_signal = new CountDownLatch(1); m_name = name; m_tracker = tracker; m_tracker.setMaxRepetitions(maxRepititions); m_maxVarsPerPdu = maxVarsPerPdu; } protected abstract WalkerPduBuilder createPduBuilder(int maxVarsPerPdu); public void start() { m_pduBuilder = createPduBuilder(m_maxVarsPerPdu); try { buildAndSendNextPdu(); } catch (Throwable e) { handleFatalError(e); } } public int getMaxVarsPerPdu() { return (m_pduBuilder == null ? m_maxVarsPerPdu : m_pduBuilder.getMaxVarsPerPdu()); } protected void buildAndSendNextPdu() throws IOException { if (m_tracker.isFinished()) { handleDone(); } else { m_pduBuilder.reset(); m_responseProcessor = m_tracker.buildNextPdu(m_pduBuilder); sendNextPdu(m_pduBuilder); } } protected abstract void sendNextPdu(WalkerPduBuilder pduBuilder) throws IOException; protected void handleDone() { finish(); } /** * <P> * Returns the success or failure code for collection of the data. * </P> */ public boolean failed() { return m_error; } public boolean timedOut() { return m_tracker.timedOut(); } protected void handleAuthError(String msg) { m_tracker.setFailed(true); processError("Authentication error processing", msg, null); } protected void handleError(String msg) { // XXX why do we set timedOut to false here? should we be doing this everywhere? m_tracker.setTimedOut(false); processError("Error retrieving", msg, null); } protected void handleError(String msg, Throwable t) { // XXX why do we set timedOut to false here? should we be doing this everywhere? m_tracker.setTimedOut(false); processError("Error retrieving", msg, t); } protected void handleFatalError(Throwable e) { m_tracker.setFailed(true); processError("Unexpected error occurred processing", e.toString(), e); } protected void handleTimeout(String msg) { m_tracker.setTimedOut(true); processError("Timeout retrieving", msg, null); } private void processError(String reason, String cause, Throwable t) { String logMessage = reason + " " + getName() + " for " + m_address + ": " + cause; m_error = true; m_errorMessage = logMessage; m_errorThrowable = t; finish(); } private void finish() { signal(); try { close(); } catch (IOException e) { log().error(getName()+": Unexpected Error occured closing SNMP session for: "+m_address, e); } } protected abstract void close() throws IOException; public String getName() { return m_name; } private void signal() { synchronized (this) { notifyAll(); } if (m_signal != null) { m_signal.countDown(); } } protected static ThreadCategory log() { return ThreadCategory.getInstance(SnmpWalker.class); } public void waitFor() throws InterruptedException { m_signal.await(); } public void waitFor(long timeout) throws InterruptedException { m_signal.await(timeout, TimeUnit.MILLISECONDS); } // processErrors returns true if we need to retry the request and false otherwise protected boolean processErrors(int errorStatus, int errorIndex) { return m_responseProcessor.processErrors(errorStatus, errorIndex); } protected void processResponse(SnmpObjId receivedOid, SnmpValue val) { m_responseProcessor.processResponse(receivedOid, val); } protected void setAddress(InetAddress address) { m_address = address; } protected InetAddress getAddress() { return m_address; } public String getErrorMessage() { return m_errorMessage; } public Throwable getErrorThrowable() { return m_errorThrowable; } }