/*
* Copyright (c) 2003, Intracom S.A. - www.intracom.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This package and its source code is available at www.jboss.org
**/
package org.jboss.jmx.adaptor.snmp.agent;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.management.Notification;
import org.jboss.bootstrap.spi.ServerConfig;
import org.jboss.jmx.adaptor.snmp.config.manager.Manager;
import org.jboss.logging.Logger;
import org.jboss.xb.binding.MappingObjectModelFactory;
import org.jboss.xb.binding.Unmarshaller;
import org.jboss.xb.binding.UnmarshallerFactory;
import org.opennms.protocols.snmp.SnmpIPAddress;
import org.opennms.protocols.snmp.SnmpPduPacket;
import org.opennms.protocols.snmp.SnmpPduTrap;
/**
* <tt>TrapEmitter</tt> is a class that manages SNMP trap emission.
*
* Currently, it allows to send V1 or V2 traps to one or more subscribed SNMP
* managers defined by their IP address, listening port number and expected
* SNMP version.
*
* @version $Revision: 80636 $
*
* @author <a href="mailto:spol@intracom.gr">Spyros Pollatos</a>
* @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
**/
public class TrapEmitter
{
/** The logger object */
private static final Logger log = Logger.getLogger(TrapEmitter.class);
/** Reference to the utilised trap factory*/
private TrapFactory trapFactory = null;
/** The actual trap factory to instantiate */
private String trapFactoryClassName = null;
/** The managers resource name */
private String managersResName = null;
/** The notification map resource name */
private String notificationMapResName = null;
/** Provides trap count */
private Counter trapCount = null;
/** Uptime clock */
private Clock uptime = null;
/** Holds the manager subscriptions. Accessed through synch'd wrapper */
private Set managers = Collections.synchronizedSet(new HashSet());
/**
* Builds a TrapEmitter object for sending SNMP V1 or V2 traps. <P>
**/
public TrapEmitter(String trapFactoryClassName,
Counter trapCount,
Clock uptime,
String managersResName,
String notificationMapResName)
{
this.trapFactoryClassName = trapFactoryClassName;
this.trapCount = trapCount;
this.uptime = uptime;
this.managersResName = managersResName;
this.notificationMapResName = notificationMapResName;
}
/**
* Complete emitter initialisation
**/
public void start()
throws Exception
{
// Load persisted manager subscriptions
load();
// Instantiate the trap factory
this.trapFactory = (TrapFactory) Class.forName(this.trapFactoryClassName,
true,
this.getClass().getClassLoader()).newInstance();
// Initialise
this.trapFactory.set(this.notificationMapResName,
this.uptime,
this.trapCount);
// Start the trap factory
this.trapFactory.start();
}
/**
* Perform shutdown
**/
public void stop()
throws Exception
{
synchronized(this.managers) {
// Recycle open sessions to managers
Iterator i = this.managers.iterator();
while (i.hasNext()) {
ManagerRecord s = (ManagerRecord)i.next();
s.closeSession();
}
// Drop all held manager records
this.managers.clear();
}
}
/**
* Intercepts the notification and after translating it to a trap sends it
* along.
*
* @param n notification to be sent
* @throws Exception if an error occurs during the preparation or
* sending of the trap
**/
public void send(Notification n)
throws Exception
{
// Beeing paranoid
synchronized(this.trapFactory) {
if(this.trapFactory == null) {
log.error("Received notifications before trap factory set. Discarding.");
return;
}
}
// Cache the translated notification
SnmpPduTrap v1TrapPdu = null;
SnmpPduPacket v2TrapPdu = null;
// Send trap. Synchronise on the subscription collection while
// iterating
synchronized(this.managers) {
// Iterate over sessions and emit the trap on each one
Iterator i = this.managers.iterator();
while (i.hasNext()) {
ManagerRecord s = (ManagerRecord)i.next();
try {
switch (s.getVersion()) {
case SnmpAgentService.SNMPV1:
if (v1TrapPdu == null)
v1TrapPdu = this.trapFactory.generateV1Trap(n);
// fix the agent ip in the trap depending on which local address is bound
v1TrapPdu.setAgentAddress(new SnmpIPAddress(s.getLocalAddress()));
// Advance the trap counter
this.trapCount.advance();
// Send
s.getSession().send(v1TrapPdu);
break;
case SnmpAgentService.SNMPV2:
if (v2TrapPdu == null)
v2TrapPdu = this.trapFactory.generateV2Trap(n);
// Advance the trap counter
this.trapCount.advance();
// Send
s.getSession().send(v2TrapPdu);
break;
default:
log.error("Skipping session: Unknown SNMP version found");
}
}
catch(MappingFailedException e) {
log.error("Translating notification - " + e.getMessage());
}
catch(Exception e) {
log.error("SNMP send error for " +
s.getAddress().toString() + ":" +
s.getPort() + ": <" + e +
">");
}
}
}
}
/**
* Load manager subscriptions
**/
private void load() throws Exception
{
log.debug("Reading resource: '" + this.managersResName + "'");
// configure ObjectModelFactory for mapping XML to POJOs
// we'll be simply getting an ArrayList of Manager objects
MappingObjectModelFactory momf = new MappingObjectModelFactory();
momf.mapElementToClass("manager-list", ArrayList.class);
momf.mapElementToClass("manager", Manager.class);
ArrayList managerList = null;
InputStream is = null;
try
{
// locate managers.xml
is = this.getClass().getResourceAsStream(this.managersResName);
// create unmarshaller
Unmarshaller unmarshaller = UnmarshallerFactory.newInstance()
.newUnmarshaller();
// let JBossXB do it's magic using the MappingObjectModelFactory
managerList = (ArrayList)unmarshaller.unmarshal(is, momf, null);
}
catch (Exception e)
{
log.error("Accessing resource '" + managersResName + "'");
throw e;
}
finally
{
if (is != null)
{
// close the XML stream
is.close();
}
}
log.debug("Found " + managerList.size() + " monitoring managers");
for (Iterator i = managerList.iterator(); i.hasNext(); )
{
// Read the monitoring manager's particulars
Manager m = (Manager)i.next();
try
{
// Create a record of the manager's interest
ManagerRecord mr = new ManagerRecord(
InetAddress.getByName(m.getAddress()),
m.getPort(),
toInetAddressWithDefaultBinding(m.getLocalAddress()),
m.getLocalPort(),
m.getVersion()
);
// Add the record to the list of monitoring managers. If
// successfull open the session to the manager as well.
if (this.managers.add(mr) == false)
{
log.warn("Ignoring duplicate manager: " + m);
}
else
{
// Open the session to the manager
mr.openSession();
}
}
catch (Exception e)
{
log.warn("Error enabling monitoring manager: " + m, e);
}
}
}
/**
* cater for possible global -b option, if no override has been specified
*/
private InetAddress toInetAddressWithDefaultBinding(String host)
throws UnknownHostException
{
if (host == null || host.length() == 0) {
String defaultBindAddress = System.getProperty(ServerConfig.SERVER_BIND_ADDRESS);
if (defaultBindAddress != null && !defaultBindAddress.equals("0.0.0.0"))
return InetAddress.getByName(defaultBindAddress);
else
return InetAddress.getLocalHost();
}
else
return InetAddress.getByName(host);
}
} // class TrapEmitter