/* * 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.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.management.Notification; import org.jboss.jmx.adaptor.snmp.config.notification.Mapping; import org.jboss.jmx.adaptor.snmp.config.notification.VarBind; import org.jboss.jmx.adaptor.snmp.config.notification.VarBindList; import org.jboss.logging.Logger; import org.jboss.xb.binding.GenericObjectModelFactory; import org.jboss.xb.binding.ObjectModelFactory; import org.jboss.xb.binding.Unmarshaller; import org.jboss.xb.binding.UnmarshallerFactory; import org.jboss.xb.binding.UnmarshallingContext; import org.opennms.protocols.snmp.SnmpPduPacket; import org.opennms.protocols.snmp.SnmpPduRequest; import org.opennms.protocols.snmp.SnmpPduTrap; import org.xml.sax.Attributes; /** * <tt>TrapFactorySupport</tt> takes care of translation of Notifications * into SNMP V1 and V2 traps * * Data Structure Guide * * It looks complicated but it ain't. The mappings are read into a structure * that follows the outline defined in the Notification.xsd. Have a look * there and in the example notificationMap.xml and you should get the picture. * As an optimization, 2 things are done: * * 1. The "NotificationType" fields of all the mappings are * read, interpreted and compiled as regular expressions. All the * instances are placed in an array and made accessible in their compiled * form * 2. The "wrapperClass" attribute is interpreted as a class name that * implements interface NotificationWrapper. An instance of each class is * created and similarly placed in an array * * This results in 2 collections one of regular expressions and one of * NotificationWrapper instances. The two collections have exactly the same * size as the collection of mappings. Obviously each read mapping has a "1-1" * correspondence with exactly 1 compiled regular expression and exactly 1 * NotificationWrapper instance. The key for the correspondence is the index: * regular expression i corresponds to mapping i that coresponds to * NotificationWrapper instance i. The loading of the 2 collections is * performed in method startService. * Checking for which mapping to apply (implemented in method findMapping) on a * notification is simple: traverse the cached regular expressions and attempt * to match the notification type against them. The FIRST match short circuits * the search and the coresponding mapping index is returned. * * @version $Revision: 44604 $ * * @author <a href="mailto:spol@intracom.gr">Spyros Pollatos</a> * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> **/ public class TrapFactorySupport implements TrapFactory { /** The logger object */ private static final Logger log = Logger.getLogger(TrapFactorySupport.class); /** Reference to SNMP variable binding factory */ private SnmpVarBindFactory snmpVBFactory = null; /** File that contains notification mappings */ private String notificationMapResName = null; /** Uptime clock */ private Clock clock = null; /** Trap counter */ private Counter trapCount = null; /** Contains the read in mappings */ private ArrayList notificationMapList = null; /** Contains the compiled regular expression type specifications */ private ArrayList mappingRegExpCache = null; /** Contains instances of the notification wrappers */ private ArrayList notificationWrapperCache = null; /** * Create TrapFactorySupport **/ public TrapFactorySupport() { this.snmpVBFactory = new SnmpVarBindFactory(); } /** * Sets the name of the file containing the notification/trap mappings, * the uptime clock and the trap counter **/ public void set(String notificationMapResName, Clock clock, Counter count) { this.notificationMapResName = notificationMapResName; this.clock = clock; this.trapCount = count; } /** * Populates the regular expression and wrapper instance collections. Note * that a failure (e.g. to compile a regular expression or to instantiate a * wrapper) generates an error message. Furthermore, the offending * expression or class are skipped and the corresponding collection entry * is null. It is the user's responsibility to track the reported errors in * the logs and act accordingly (i.e. correct them and restart). If not the * corresponding mappings are effectively void and will NOT have effect. **/ public void start() throws Exception { log.debug("Reading resource: '" + notificationMapResName + "'"); ObjectModelFactory omf = new NotificationBinding(); InputStream is = null; try { // locate notifications.xml is = this.getClass().getResourceAsStream(notificationMapResName); // create unmarshaller Unmarshaller unmarshaller = UnmarshallerFactory.newInstance().newUnmarshaller(); // let JBossXB do it's magic using the MappingObjectModelFactory this.notificationMapList = (ArrayList)unmarshaller.unmarshal(is, omf, null); } catch (Exception e) { log.error("Accessing resource '" + notificationMapResName + "'"); throw e; } finally { if (is != null) { // close the XML stream is.close(); } } log.debug("Found " + notificationMapList.size() + " notification mappings"); // Initialise the cache with the compiled regular expressions denoting // notification type specifications this.mappingRegExpCache = new ArrayList(notificationMapList.size()); // Initialise the cache with the instantiated notification wrappers this.notificationWrapperCache = new ArrayList(notificationMapList.size()); for (Iterator i = notificationMapList.iterator(); i.hasNext(); ) { Mapping mapping = (Mapping)i.next(); // Compile and add the regular expression String notificationType = mapping.getNotificationType(); try { Pattern re = Pattern.compile(notificationType); this.mappingRegExpCache.add(re); } catch (PatternSyntaxException e) { // Fill the slot to keep index count correct this.mappingRegExpCache.add(null); log.warn("Error compiling notification mapping for type: " + notificationType, e); } // Instantiate and add the wrapper // Read wrapper class name String wrapperClassName = mapping.getVarBindList().getWrapperClass(); log.debug("notification wrapper class: " + wrapperClassName); try { NotificationWrapper wrapper = (NotificationWrapper)Class.forName(wrapperClassName, true, this.getClass().getClassLoader()).newInstance(); // Initialise it wrapper.set(this.clock, this.trapCount); // Add the wrapper to the cache this.notificationWrapperCache.add(wrapper); } catch (Exception e) { // Fill the slot to keep index count correct this.notificationWrapperCache.add(null); log.warn("Error compiling notification mapping for type: " + notificationType, e); } } log.debug("Trap factory going active"); } /** * Locate mapping applicable for the incoming notification. Key is the * notification's type * * @param n the notification to be examined * @return the index of the mapping * @throws IndexOutOfBoundsException if no mapping found **/ private int findMappingIndex(Notification n) throws IndexOutOfBoundsException { // Sequentially check the notification type against the compiled // regular expressions. On first match return the coresponding mapping // index for (int i = 0; i < notificationMapList.size(); i++) { Pattern p = (Pattern) this.mappingRegExpCache.get(i); if (p != null) { Matcher m = p.matcher(n.getType()); if (m.matches()) { if (log.isTraceEnabled()) log.trace("Match for '" + n.getType() + "' on mapping " + i); return i; } } } // Signal "no mapping found" throw new IndexOutOfBoundsException(); } /** * Traslates a Notification to an SNMP V1 trap. **/ public SnmpPduTrap generateV1Trap(Notification n) throws MappingFailedException { if (log.isTraceEnabled()) log.trace("generateV1Trap"); // Locate mapping for incomming event int index = -1; try { index = findMappingIndex(n); } catch (IndexOutOfBoundsException e) { throw new MappingFailedException("No mapping found for notification type: '" + n.getType() + "'"); } Mapping m = (Mapping)this.notificationMapList.get(index); // Create trap SnmpPduTrap trapPdu = new SnmpPduTrap(); trapPdu.setTimeStamp(this.clock.uptime()); // Organise the 'variable' payload trapPdu.setGeneric(m.getGeneric()); trapPdu.setSpecific(m.getSpecific()); trapPdu.setEnterprise(m.getEnterprise()); // Append the specified varbinds. Get varbinds from mapping and for // each one of the former use the wrapper to get the corresponding // values // Get the coresponding wrapper to get access to notification payload NotificationWrapper wrapper = (NotificationWrapper)this.notificationWrapperCache.get(index); if(wrapper != null) { // Prime the wrapper with the notification contents wrapper.prime(n); // Iterate through mapping specified varbinds and organise values // for each List vbList = m.getVarBindList().getVarBindList(); for (int i = 0; i < vbList.size(); i++) { VarBind vb = (VarBind)vbList.get(i); // Append the var bind. Interrogate read vb for OID and // variable tag. The later is used as the key passed to the // wrapper in order for it to locate the required value. That // value and the aforementioned OID are used to generate the // variable binding trapPdu.addVarBind( this.snmpVBFactory.make(vb.getOid(), wrapper.get(vb.getTag()))); } } else { throw new MappingFailedException( "Varbind mapping failure: null wrapper defined for " + " notification type '" + m.getNotificationType() + "'" ); } return trapPdu; } /** * Traslates a Notification to an SNMP V2 trap. * * TODO: how do you get timestamp, generic, and specific stuff in the trap **/ public SnmpPduPacket generateV2Trap(Notification n) throws MappingFailedException { if (log.isTraceEnabled()) log.trace("generateV2Trap"); // Locate mapping for incomming event int index = -1; try { index = findMappingIndex(n); } catch (IndexOutOfBoundsException e) { throw new MappingFailedException( "No mapping found for notification type: '" + n.getType() + "'"); } Mapping m = (Mapping)this.notificationMapList.get(index); // Create trap SnmpPduRequest trapPdu = new SnmpPduRequest(SnmpPduPacket.V2TRAP); // Append the specified varbinds. Get varbinds from mapping and for // each one of the former use the wrapper to get data from the // notification // Get the coresponding wrapper NotificationWrapper wrapper = (NotificationWrapper)this.notificationWrapperCache.get(index); if (wrapper != null) { // Prime the wrapper with the notification contents wrapper.prime(n); List vbList = m.getVarBindList().getVarBindList(); for (int i = 0; i < vbList.size(); i++) { VarBind vb = (VarBind)vbList.get(i); // Append the var bind. Interrogate read vb for OID and // variable tag. The later is used as the key passed to the // wrapper in order for it to locate the required value. That // value and the aforementioned OID are used to generate the // variable binding trapPdu.addVarBind( this.snmpVBFactory.make(vb.getOid(), wrapper.get(vb.getTag()))); } } else { log.warn("Varbind mapping failure: null wrapper defined for " + " notification type '" + m.getNotificationType() + "'" ); } return trapPdu; } /** * Utility class used by JBossXB to help parse notifications.xml */ private static class NotificationBinding implements GenericObjectModelFactory { // GenericObjectModelFactory implementation ---------------------- public Object completeRoot(Object root, UnmarshallingContext ctx, String uri, String name) { return root; } public Object newRoot(Object root, UnmarshallingContext navigator, String namespaceURI, String localName, Attributes attrs) { ArrayList notifList; if (root == null) { root = notifList = new ArrayList(); } else { notifList = (ArrayList) root; } return root; } public Object newChild(Object parent, UnmarshallingContext navigator, String namespaceURI, String localName, Attributes attrs) { Object child = null; if ("mapping".equals(localName)) { Mapping m = new Mapping(); child = m; } else if ("var-bind-list".equals(localName)) { VarBindList vblist = new VarBindList(); child = vblist; if (attrs.getLength() > 0) { for (int i = 0; i < attrs.getLength(); i++) { if ("wrapper-class".equals(attrs.getLocalName(i))) { vblist.setWrapperClass(attrs.getValue(i)); } } } // check that wrapper-class is set if (vblist.getWrapperClass() == null) { throw new RuntimeException("'wrapper-class' must be set at 'var-bind-list' element"); } } else if ("var-bind".equals(localName)) { VarBind vb = new VarBind(); child = vb; } return child; } public void addChild(Object parent, Object child, UnmarshallingContext navigator, String namespaceURI, String localName) { if (parent instanceof ArrayList) { ArrayList notifList = (ArrayList)parent; if (child instanceof Mapping) { notifList.add(child); } } else if (parent instanceof Mapping) { Mapping m = (Mapping)parent; if (child instanceof VarBindList) { m.setVarBindList((VarBindList)child); } } else if (parent instanceof VarBindList) { VarBindList vblist = (VarBindList)parent; if (child instanceof VarBind) { vblist.addVarBind((VarBind)child); } } } public void setValue(Object o, UnmarshallingContext navigator, String namespaceURI, String localName, String value) { if (o instanceof Mapping) { Mapping m = (Mapping)o; if ("notification-type".equals(localName)) { m.setNotificationType(value); } else if ("generic".equals(localName)) { m.setGeneric(Integer.parseInt(value)); } else if ("specific".equals(localName)) { m.setSpecific(Integer.parseInt(value)); } else if ("enterprise".equals(localName)) { m.setEnterprise(value); } } else if (o instanceof VarBind) { VarBind vb = (VarBind)o; if ("tag".equals(localName)) { vb.setTag(value); } else if ("oid".equals(localName)) { vb.setOid(value); } } } public Object completedRoot(Object root, UnmarshallingContext navigator, String namespaceURI, String localName) { return root; } } } // class TrapFactorySupport