001: /*
002: * Copyright (c) 2003, Intracom S.A. - www.intracom.com
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * This package and its source code is available at www.jboss.org
019: */
020: package org.jboss.jmx.adaptor.snmp.agent;
021:
022: import java.net.InetAddress;
023: import java.net.UnknownHostException;
024:
025: import javax.management.Notification;
026: import javax.management.ObjectName;
027:
028: import org.jboss.system.ListenerServiceMBeanSupport;
029: import org.jboss.system.server.ServerConfig;
030: import org.opennms.protocols.snmp.SnmpAgentSession;
031: import org.opennms.protocols.snmp.SnmpPeer;
032: import org.opennms.protocols.snmp.SnmpSMI;
033:
034: /**
035: * <tt>SnmpAgentService</tt> is an MBean class implementing an SNMP agent.
036: *
037: * It allows to send V1 or V2 traps to one or more SNMP managers defined
038: * by their IP address, listening port number and expected SNMP version.
039: *
040: * It support mapping SNMP get/set requests JMX mbean attribute get/sets.
041: *
042: * @jmx:mbean
043: * extends="org.jboss.system.ListenerServiceMBean"
044: *
045: * @author <a href="mailto:spol@intracom.gr">Spyros Pollatos</a>
046: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
047: * @author <a href="mailto:krishnaraj@ieee.org">Krishnaraj S</a>
048: * @version $Revision: 44599 $
049: */
050: public class SnmpAgentService extends ListenerServiceMBeanSupport
051: implements SnmpAgentServiceMBean {
052: /** Supported versions */
053: public static final int SNMPV1 = 1;
054: public static final int SNMPV2 = 2;
055:
056: /** Default communities */
057: public static final String DEFAULT_READ_COMMUNITY = "public";
058: public static final String DEFAULT_WRITE_COMMUNITY = "private";
059:
060: // Private data --------------------------------------------------
061:
062: /** Time keeping*/
063: private Clock clock = null;
064:
065: /** Trap counter */
066: private Counter trapCounter = null;
067:
068: /** Name of the file containing SNMP manager specifications */
069: private String managersResName = null;
070:
071: /** Name of resource file containing notification to trap mappings */
072: private String notificationMapResName = null;
073:
074: /** Name of resource file containing get/set mappings */
075: private String requestHandlerResName = null;
076:
077: /** Name of the trap factory class to be utilised */
078: private String trapFactoryClassName = null;
079:
080: /** Name of request handler implementation class */
081: private String requestHandlerClassName = null;
082:
083: /** The SNMP read community to use */
084: private String readCommunity = DEFAULT_READ_COMMUNITY;
085:
086: /** The SNMP write community to use */
087: private String writeCommunity = DEFAULT_WRITE_COMMUNITY;
088:
089: /** Controls the request processing thread pool */
090: private int numberOfThreads = 1;
091:
092: /** Agent listening port */
093: private int port = 1161;
094:
095: /** Agent SNMP protocol version */
096: private int snmpVersion = SNMPV1;
097:
098: /** The interface to bind, useful for multi-homed hosts */
099: private InetAddress bindAddress;
100:
101: /** Name of the utilised timer MBean */
102: private ObjectName timerName = null;
103:
104: /** Heartbeat emission period (in seconds) and switch */
105: private int heartBeatPeriod = 60;
106:
107: /** Dynamic subscriptions flag */
108: private boolean dynamicSubscriptions = true;
109:
110: /** Reference to heartbeat emission controller */
111: private Heartbeat heartbeat = null;
112:
113: /** The trap emitting subsystem*/
114: private TrapEmitter trapEmitter = null;
115:
116: /** the snmp agent session for handling get/set requests */
117: private SnmpAgentSession agentSession = null;
118:
119: /** the request handler instance handling get/set requests */
120: private RequestHandler requestHandler;
121:
122: // Constructors --------------------------------------------------
123:
124: /**
125: * Default CTOR
126: */
127: public SnmpAgentService() {
128: // empty
129: }
130:
131: // Attributes ----------------------------------------------------
132:
133: /**
134: * Gets the heartbeat switch
135: *
136: * @jmx:managed-attribute
137: */
138: public int getHeartBeatPeriod() {
139: return this .heartBeatPeriod;
140: }
141:
142: /**
143: * Sets the heartbeat period (in seconds) switch
144: *
145: * @jmx:managed-attribute
146: */
147: public void setHeartBeatPeriod(int heartBeatPeriod) {
148: this .heartBeatPeriod = heartBeatPeriod;
149: }
150:
151: /**
152: * Returns the difference, measured in milliseconds, between the
153: * instantiation time and midnight, January 1, 1970 UTC.
154: *
155: * @jmx:managed-attribute
156: */
157: public long getInstantiationTime() {
158: return this .clock.instantiationTime();
159: }
160:
161: /**
162: * Returns the up-time
163: *
164: * @jmx:managed-attribute
165: */
166: public long getUptime() {
167: return this .clock.uptime();
168: }
169:
170: /**
171: * Returns the current trap counter reading
172: *
173: * @jmx:managed-attribute
174: */
175: public long getTrapCount() {
176: return this .trapCounter.peek();
177: }
178:
179: /**
180: * Sets the name of the file containing SNMP manager specifications
181: *
182: * @jmx:managed-attribute
183: */
184: public void setManagersResName(String managersResName) {
185: this .managersResName = managersResName;
186: }
187:
188: /**
189: * Gets the name of the file containing SNMP manager specifications
190: *
191: * @jmx:managed-attribute
192: */
193: public String getManagersResName() {
194: return this .managersResName;
195: }
196:
197: /**
198: * Sets the name of the file containing the notification/trap mappings
199: *
200: * @jmx:managed-attribute
201: */
202: public void setNotificationMapResName(String notificationMapResName) {
203: this .notificationMapResName = notificationMapResName;
204: }
205:
206: /**
207: * Gets the name of the file containing the notification/trap mappings
208: *
209: * @jmx:managed-attribute
210: */
211: public String getNotificationMapResName() {
212: return this .notificationMapResName;
213: }
214:
215: /**
216: * Sets the utilised trap factory name
217: *
218: * @jmx:managed-attribute
219: */
220: public void setTrapFactoryClassName(String name) {
221: this .trapFactoryClassName = name;
222: }
223:
224: /**
225: * Gets the utilised trap factory name
226: *
227: * @jmx:managed-attribute
228: */
229: public String getTrapFactoryClassName() {
230: return this .trapFactoryClassName;
231: }
232:
233: /**
234: * Sets the utilised timer MBean name
235: *
236: * @jmx:managed-attribute
237: */
238: public void setTimerName(ObjectName timerName) {
239: this .timerName = timerName;
240: }
241:
242: /**
243: * Gets the utilised timer MBean name
244: *
245: * @jmx:managed-attribute
246: */
247: public ObjectName getTimerName() {
248: return this .timerName;
249: }
250:
251: /**
252: * Sets the agent bind address
253: *
254: * @jmx:managed-attribute
255: */
256: public void setBindAddress(String bindAddress)
257: throws UnknownHostException {
258: this .bindAddress = toInetAddress(bindAddress);
259: }
260:
261: /**
262: * Gets the agent bind address
263: *
264: * @jmx:managed-attribute
265: */
266: public String getBindAddress() {
267: String address = null;
268:
269: if (this .bindAddress != null)
270: address = this .bindAddress.getHostAddress();
271:
272: return address;
273: }
274:
275: /**
276: * Sets the number of threads in the agent request processing thread pool
277: *
278: * @jmx:managed-attribute
279: */
280: public void setNumberOfThreads(int numberOfThreads) {
281: if (numberOfThreads > 0 && numberOfThreads <= 12) {
282: this .numberOfThreads = numberOfThreads;
283: }
284: }
285:
286: /**
287: * Gets the number of threads in the agent requests processing thread pool
288: *
289: * @jmx:managed-attribute
290: */
291: public int getNumberOfThreads() {
292: return numberOfThreads;
293: }
294:
295: /**
296: * Sets the agent listening port number
297: *
298: * @jmx:managed-attribute
299: */
300: public void setPort(int port) {
301: if (port >= 0) {
302: this .port = port;
303: }
304: }
305:
306: /**
307: * Gets the agent listening port number
308: *
309: * @jmx:managed-attribute
310: */
311: public int getPort() {
312: return port;
313: }
314:
315: /**
316: * Sets the snmp protocol version
317: *
318: * @jmx:managed-attribute
319: */
320: public void setSnmpVersion(int snmpVersion) {
321: switch (snmpVersion) {
322: case SNMPV2:
323: this .snmpVersion = SNMPV2;
324: break;
325:
326: default:
327: this .snmpVersion = SNMPV1;
328: break;
329: }
330: }
331:
332: /**
333: * Gets the snmp protocol version
334: *
335: * @jmx:managed-attribute
336: */
337: public int getSnmpVersion() {
338: return snmpVersion;
339: }
340:
341: /**
342: * Sets the read community (no getter)
343: *
344: * @jmx:managed-attribute
345: */
346: public void setReadCommunity(String readCommunity) {
347: if (readCommunity != null && readCommunity.length() > 0) {
348: this .readCommunity = readCommunity;
349: }
350: }
351:
352: /**
353: * Sets the write community (no getter)
354: * @jmx:managed-attribute
355: */
356: public void setWriteCommunity(String writeCommunity) {
357: if (writeCommunity != null && writeCommunity.length() > 0) {
358: this .writeCommunity = writeCommunity;
359: }
360: }
361:
362: /**
363: * Sets the RequestHandler implementation class
364: *
365: * @jmx:managed-attribute
366: */
367: public void setRequestHandlerClassName(
368: String requestHandlerClassName) {
369: this .requestHandlerClassName = requestHandlerClassName;
370: }
371:
372: /**
373: * Gets the RequestHandler implementation class
374: *
375: * @jmx:managed-attribute
376: */
377: public String getRequestHandlerClassName() {
378: return requestHandlerClassName;
379: }
380:
381: /**
382: * Sets the resource file name containing get/set mappings
383: *
384: * @jmx:managed-attribute
385: */
386: public void setRequestHandlerResName(String requestHandlerResName) {
387: this .requestHandlerResName = requestHandlerResName;
388: }
389:
390: /**
391: * Gets the resource file name containing get/set mappings
392: *
393: * @jmx:managed-attribute
394: */
395: public String getRequestHandlerResName() {
396: return requestHandlerResName;
397: }
398:
399: /**
400: * Enables/disables dynamic subscriptions
401: *
402: * @jmx:managed-attribute
403: */
404: public void setDynamicSubscriptions(boolean dynamicSubscriptions) {
405: this .dynamicSubscriptions = dynamicSubscriptions;
406: }
407:
408: /**
409: * Gets the dynamic subscriptions status
410: *
411: * @jmx:managed-attribute
412: */
413: public boolean getDynamicSubscriptions() {
414: return this .dynamicSubscriptions;
415: }
416:
417: // Operations ----------------------------------------------------
418:
419: /**
420: * Reconfigures the RequestHandler, reponsible for handling get requests etc.
421: *
422: * @jmx:managed-operation
423: */
424: public void reconfigureRequestHandler() throws Exception {
425: if (requestHandler instanceof Reconfigurable)
426: ((Reconfigurable) requestHandler)
427: .reconfigure(getRequestHandlerResName());
428: else
429: throw new UnsupportedOperationException(
430: "Request handler is not Reconfigurable");
431: }
432:
433: // Lifecycle operations ------------------------------------------
434:
435: /**
436: * Perform service start-up
437: */
438: protected void startService() throws Exception {
439: // initialize clock and trapCounter
440: this .clock = new Clock();
441: this .trapCounter = new Counter(0);
442:
443: // Notification subscription are handled by
444: // ListenerServiceMBeanSupport baseclass
445:
446: log.debug("Instantiating trap emitter ...");
447: this .trapEmitter = new TrapEmitter(this
448: .getTrapFactoryClassName(), this .trapCounter,
449: this .clock, this .getManagersResName(), this
450: .getNotificationMapResName());
451:
452: // Start trap emitter
453: log.debug("Starting trap emitter ...");
454: this .trapEmitter.start();
455:
456: // Get the heartbeat going
457: this .heartbeat = new Heartbeat(this .getServer(), this
458: .getTimerName(), this .getHeartBeatPeriod());
459:
460: log.debug("Starting heartbeat controller ...");
461: heartbeat.start();
462:
463: // subscribe for notifications, with the option for dynamic subscriptions
464: super .subscribe(this .dynamicSubscriptions);
465:
466: // initialise the snmp agent
467: log.debug("Starting snmp agent ...");
468: startAgent();
469:
470: log.info("SNMP agent going active");
471:
472: // Send the cold start!
473: this .sendNotification(new Notification(EventTypes.COLDSTART,
474: this , getNextNotificationSequenceNumber()));
475: }
476:
477: /**
478: * Perform service shutdown
479: */
480: protected void stopService() throws Exception {
481: // unsubscribe for notifications
482: super .unsubscribe();
483:
484: log.debug("Stopping heartbeat controller ...");
485: this .heartbeat.stop();
486: this .heartbeat = null; // gc
487:
488: log.debug("Stopping trap emitter ...");
489: this .trapEmitter.stop();
490: this .trapEmitter = null;
491:
492: log.debug("Stopping snmp agent ...");
493: this .agentSession.close();
494: this .agentSession = null;
495:
496: log.info("SNMP agent stopped");
497: }
498:
499: // Notification handling -----------------------------------------
500:
501: /**
502: * All notifications are intercepted here and are routed for emission.
503: */
504: public void handleNotification2(Notification n, Object handback) {
505: if (log.isTraceEnabled()) {
506: log.trace("Received notification: <" + n + "> Payload "
507: + "TS: <" + n.getTimeStamp() + "> " + "SN: <"
508: + n.getSequenceNumber() + "> " + "T: <"
509: + n.getType() + ">");
510: }
511:
512: try {
513: this .trapEmitter.send(n);
514: } catch (Exception e) {
515: log.error("Sending trap", e);
516: }
517: }
518:
519: // Private -------------------------------------------------------
520:
521: /**
522: * Start the embedded agent
523: */
524: private void startAgent() throws Exception {
525: // cater for possible global -b option, if no override has been specified
526: InetAddress address = this .bindAddress != null ? this .bindAddress
527: : toInetAddress(System
528: .getProperty(ServerConfig.SERVER_BIND_ADDRESS));
529:
530: // the listening address
531: SnmpPeer peer = new SnmpPeer(address, this .port);
532:
533: // set community strings and protocol version
534: peer.getParameters().setReadCommunity(this .readCommunity);
535: peer.getParameters().setWriteCommunity(this .writeCommunity);
536: peer.getParameters().setVersion(
537: this .snmpVersion == SNMPV2 ? SnmpSMI.SNMPV2
538: : SnmpSMI.SNMPV1);
539:
540: // Instantiate and initialize the RequestHandler implementation
541: requestHandler = (RequestHandler) Class.forName(
542: this .requestHandlerClassName, true,
543: this .getClass().getClassLoader()).newInstance();
544: requestHandler.initialize(this .requestHandlerResName, this
545: .getServer(), this .log, this .clock);
546:
547: // Instantiate the AgentSession with an optional thread pool
548: this .agentSession = this .numberOfThreads > 1 ? new SnmpAgentSession(
549: requestHandler, peer, this .numberOfThreads)
550: : new SnmpAgentSession(requestHandler, peer);
551: }
552:
553: /**
554: * Safely convert a host string to InetAddress or null
555: */
556: private InetAddress toInetAddress(String host)
557: throws UnknownHostException {
558: if (host == null || host.length() == 0)
559: return null;
560: else
561: return InetAddress.getByName(host);
562: }
563:
564: }
|