KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > snmp4j > agent > BaseAgent


1 /*_############################################################################
2   _##
3   _## SNMP4J-Agent - BaseAgent.java
4   _##
5   _## Copyright (C) 2005-2007 Frank Fock (SNMP4J.org)
6   _##
7   _## Licensed under the Apache License, Version 2.0 (the "License");
8   _## you may not use this file except in compliance with the License.
9   _## You may obtain a copy of the License at
10   _##
11   _## http://www.apache.org/licenses/LICENSE-2.0
12   _##
13   _## Unless required by applicable law or agreed to in writing, software
14   _## distributed under the License is distributed on an "AS IS" BASIS,
15   _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   _## See the License for the specific language governing permissions and
17   _## limitations under the License.
18   _##
19   _##########################################################################*/

20
21 package org.snmp4j.agent;
22
23 import java.io.*;
24
25 import org.snmp4j.*;
26 import org.snmp4j.agent.io.*;
27 import org.snmp4j.agent.mo.snmp.*;
28 import org.snmp4j.agent.mo.snmp4j.*;
29 import org.snmp4j.log.*;
30 import org.snmp4j.mp.*;
31 import org.snmp4j.security.*;
32 import org.snmp4j.smi.*;
33 import org.snmp4j.transport.*;
34
35 /**
36  * The <code>BaseAgent</code> abstract class defines a framework for writing
37  * SNMP agents using the SNMP4J-Agent API. To implement your own SNMP agent,
38  * extend this class and implement the abstract methods defined by BaseAgent.
39  * The hook methods do not need any specific implementation. They only provide
40  * a defined mechanism to customize your agent.
41  *
42  * @author Frank Fock
43  * @version 1.0
44  */

45 public abstract class BaseAgent implements Runnable JavaDoc, MOPersistenceProvider {
46
47   private static final LogAdapter logger =
48       LogFactory.getLogger(BaseAgent.class);
49
50   public static final int STATE_CREATED = 0;
51   public static final int STATE_INIT_STARTED = 10;
52   public static final int STATE_INIT_FINISHED = 20;
53   public static final int STATE_RUNNING = 40;
54   public static final int STATE_STOPPED = 30;
55
56   protected SNMPv2MIB snmpv2MIB;
57   protected SnmpFrameworkMIB snmpFrameworkMIB;
58   protected SnmpTargetMIB snmpTargetMIB;
59   protected SnmpNotificationMIB snmpNotificationMIB;
60   protected SnmpProxyMIB snmpProxyMIB;
61   protected SnmpCommunityMIB snmpCommunityMIB;
62   protected Snmp4jLogMib snmp4jLogMIB;
63   protected Snmp4jConfigMib snmp4jConfigMIB;
64   protected UsmMIB usmMIB;
65   protected VacmMIB vacmMIB;
66   protected DefaultMOServer server;
67   protected Snmp session;
68   protected TransportMapping[] transportMappings;
69   protected MessageDispatcherImpl dispatcher;
70   protected CommandProcessor agent;
71
72   protected MPv3 mpv3;
73   protected USM usm;
74
75   protected File configFile;
76   protected File bootCounterFile;
77   protected NotificationOriginator notificationOriginator;
78   protected ProxyForwarder defaultProxyForwarder;
79
80   protected OctetString sysDescr =
81       new OctetString("SNMP4J-Agent - "+
82                       System.getProperty("os.name","")+
83                       " - "+System.getProperty("os.arch")+
84                       " - "+System.getProperty("os.version"));
85   protected OID sysOID = new OID("1.3.6.1.4.1.4976");
86   protected Integer32 sysServices = new Integer32(10);
87
88   protected int agentState = STATE_CREATED;
89
90   protected OctetString defaultContext;
91
92   /**
93    * Creates a base agent with a {@link DefaultMOServer} as {@link MOServer}.
94    * To use a different server implementation, modify the {@link #server} member
95    * after construction.
96    */

97   protected BaseAgent() {
98     this.server = new DefaultMOServer();
99   }
100
101   /**
102    * Creates a base agent with boot-counter, config file, and a CommandProcessor
103    * for processing SNMP requests.
104    *
105    * @param bootCounterFile
106    * a file with serialized boot-counter information (read/write). If the
107    * file does not exist it is created on shutdown of the agent.
108    * @param configFile
109    * a file with serialized configuration information (read/write). If the
110    * file does not exist it is created on shutdown of the agent.
111    * @param commandProcessor
112    * the <code>CommandProcessor</code> instance that handles the SNMP
113    * requests.
114    */

115   protected BaseAgent(File bootCounterFile,
116                       File configFile,
117                       CommandProcessor commandProcessor) {
118     this();
119     this.configFile = configFile;
120     this.bootCounterFile = bootCounterFile;
121     this.agent = commandProcessor;
122   }
123
124   /**
125    * Initialize transport mappings, message dispatcher, basic MIB modules,
126    * proxy forwarder, VACM and USM security, and custom MIB modules and objects
127    * provided by sub-classes.
128    *
129    * @throws IOException
130    * if initialization fails because transport initialization fails.
131    */

132   public void init() throws IOException {
133     agentState = STATE_INIT_STARTED;
134     initTransportMappings();
135     initMessageDispatcher();
136     server.addContext(new OctetString());
137     snmpv2MIB = new SNMPv2MIB(sysDescr, sysOID, sysServices);
138
139     // register Snmp counters for updates
140
dispatcher.addCounterListener(snmpv2MIB);
141     agent.addCounterListener(snmpv2MIB);
142     snmpFrameworkMIB =
143         new SnmpFrameworkMIB((USM)
144                              mpv3.getSecurityModel(SecurityModel.SECURITY_MODEL_USM),
145                              dispatcher.getTransportMappings());
146     usmMIB = new UsmMIB(usm, SecurityProtocols.getInstance());
147     usm.addUsmUserListener(usmMIB);
148
149     vacmMIB = new VacmMIB(server);
150     snmpTargetMIB = new SnmpTargetMIB(session);
151     snmpNotificationMIB = new SnmpNotificationMIB();
152     snmpCommunityMIB = new SnmpCommunityMIB(snmpTargetMIB);
153
154     snmp4jLogMIB = new Snmp4jLogMib();
155     snmp4jConfigMIB = new Snmp4jConfigMib(snmpv2MIB.getSysUpTime());
156     snmp4jConfigMIB.setSnmpCommunityMIB(snmpCommunityMIB);
157     snmp4jConfigMIB.setDefaultConfigFile(configFile);
158     snmp4jConfigMIB.setPersistenceProvider(
159       Snmp4jConfigMib.Snmp4jCfgStorageFormatEnum.binary, this);
160     snmpProxyMIB = new SnmpProxyMIB();
161     notificationOriginator =
162         new NotificationOriginatorImpl(session, vacmMIB,
163                                        snmpv2MIB.getSysUpTime(),
164                                        snmpTargetMIB, snmpNotificationMIB);
165     setupDefaultProxyForwarder();
166     // add USM users
167
addUsmUser(usm);
168     // add SNMPv1/v2c community to SNMPv3 security name mappings
169
addCommunities(snmpCommunityMIB);
170     addViews(vacmMIB);
171     addNotificationTargets(snmpTargetMIB, snmpNotificationMIB);
172
173     registerSnmpMIBs();
174   }
175
176   /**
177    * This method can be overwritten by a subagent to specify the contexts
178    * each MIB module (group) will be registered to.
179    *
180    * @param mibGroup
181    * a group of {@link ManagedObject}s (i.e., a MIB module).
182    * @return
183    * the context for which the module should be registered.
184    * @since 1.1
185    */

186   protected OctetString getContext(MOGroup mibGroup) {
187     return getDefaultContext();
188   }
189
190   /**
191    * Register the basic MIB modules at the agent's <code>MOServer</code>.
192    */

193   protected void registerSnmpMIBs() {
194     try {
195       snmpTargetMIB.registerMOs(server, getContext(snmpTargetMIB));
196       snmpNotificationMIB.registerMOs(server, getContext(snmpNotificationMIB));
197       vacmMIB.registerMOs(server, getContext(vacmMIB));
198       usmMIB.registerMOs(server, getContext(usmMIB));
199       snmpv2MIB.registerMOs(server, getContext(snmpv2MIB));
200       snmpFrameworkMIB.registerMOs(server, getContext(snmpFrameworkMIB));
201       snmpCommunityMIB.registerMOs(server, getContext(snmpCommunityMIB));
202       snmp4jLogMIB.registerMOs(server, getContext(snmp4jLogMIB));
203       snmp4jConfigMIB.registerMOs(server, getContext(snmp4jConfigMIB));
204       snmpProxyMIB.registerMOs(server, getContext(snmpProxyMIB));
205       registerManagedObjects();
206     }
207     catch (DuplicateRegistrationException ex) {
208       ex.printStackTrace();
209     }
210   }
211
212   /**
213    * Unregister the basic MIB modules from the agent's <code>MOServer</code>.
214    */

215   protected void unregisterSnmpMIBs() {
216     snmpTargetMIB.unregisterMOs(server, getContext(snmpTargetMIB));
217     snmpNotificationMIB.unregisterMOs(server, getContext(snmpNotificationMIB));
218     vacmMIB.unregisterMOs(server, getContext(vacmMIB));
219     usmMIB.unregisterMOs(server, getContext(usmMIB));
220     snmpv2MIB.unregisterMOs(server, getContext(snmpv2MIB));
221     snmpFrameworkMIB.unregisterMOs(server, getContext(snmpFrameworkMIB));
222     snmpCommunityMIB.unregisterMOs(server, getContext(snmpCommunityMIB));
223     snmp4jLogMIB.unregisterMOs(server, getContext(snmp4jLogMIB));
224     snmp4jConfigMIB.unregisterMOs(server, getContext(snmp4jConfigMIB));
225     snmpProxyMIB.unregisterMOs(server, getContext(snmpProxyMIB));
226     unregisterManagedObjects();
227   }
228
229   /**
230    * Register additional managed objects at the agent's server.
231    */

232   protected abstract void registerManagedObjects();
233
234   /**
235    * Unregister additional managed objects from the agent's server.
236    */

237   protected abstract void unregisterManagedObjects();
238
239   /**
240    * Creates and registers the default proxy forwarder application
241    * ({@link ProxyForwarderImpl}).
242    */

243   protected void setupDefaultProxyForwarder() {
244     defaultProxyForwarder = new ProxyForwarderImpl(session, snmpProxyMIB,
245                                                    snmpTargetMIB);
246     agent.addProxyForwarder(defaultProxyForwarder,
247                             null, ProxyForwarder.PROXY_TYPE_ALL);
248     ((ProxyForwarderImpl)defaultProxyForwarder).addCounterListener(snmpv2MIB);
249   }
250
251   /**
252    * Loads the configuration using the specified import mode from the set
253    * config file.
254    * @param importMode
255    * one of the import modes defined by {@link ImportModes}.
256    */

257   public void loadConfig(int importMode) {
258     try {
259       FileInputStream fis = null;
260       fis = new FileInputStream(configFile);
261       loadConfig(fis, importMode);
262       fis.close();
263     }
264     catch (FileNotFoundException ex) {
265       logger.error(ex);
266     }
267     catch (IOException ex) {
268       logger.error(ex);
269     }
270   }
271
272   /**
273    * Loads the configuration using the specified import mode from the set
274    * config file.
275    * @param configFile
276    * the config file (input stream) containing the serialized
277    * configuration data.
278    * @param importMode
279    * one of the import modes defined by {@link ImportModes}.
280    */

281   public void loadConfig(InputStream configFile, int importMode)
282       throws IOException
283   {
284     ObjectInputStream ois = new ObjectInputStream(configFile);
285     DefaultMOInput is = new DefaultMOInput(ois);
286     is.setOverwriteMode(importMode);
287     MOServerPersistence p = new MOServerPersistence(server);
288     p.loadData(is);
289     ois.close();
290   }
291
292   /**
293    * Save the current (serializable) managed object configuration into
294    * the config file.
295    */

296   public void saveConfig() {
297     FileOutputStream fos = null;
298     try {
299       fos = new FileOutputStream(configFile);
300       saveConfig(fos);
301       fos.flush();
302       fos.close();
303     }
304     catch (FileNotFoundException ex) {
305       logger.error(ex);
306     }
307     catch (IOException ex) {
308       logger.error(ex);
309     }
310     finally {
311       if (fos != null) {
312         try {
313           fos.close();
314         }
315         catch (IOException ex1) {
316           logger.warn(ex1);
317         }
318       }
319     }
320   }
321
322   /**
323    * Saves the current (serializable) managed object configuration into the
324    * specified config file (output stream).
325    * @param configFile
326    * an OutputStream to hold the serialized MIB data.
327    * @throws IOException
328    * if the supplied output stream throws an IOException during write
329    * operation.
330    */

331   public void saveConfig(OutputStream configFile) throws IOException {
332     ObjectOutputStream oos = new ObjectOutputStream(configFile);
333     DefaultMOOutput os = new DefaultMOOutput(oos);
334     MOServerPersistence p = new MOServerPersistence(server);
335     p.saveData(os);
336     oos.flush();
337   }
338
339   /**
340    * Adds a shutdown hook that saves the internal config into the config file
341    * when a SIGTERM (Ctrl-C) is terminating the agent.
342    */

343   protected void addShutdownHook() {
344     Runtime.getRuntime().addShutdownHook(new Thread JavaDoc() {
345       public void run() {
346         saveConfig();
347       }
348     });
349   }
350
351   /**
352    * Finishes intialization of the agent by connecting server and command
353    * processor, setting up USM, VACM, notification targets, and finally sending
354    * a coldStart notification to configured targets.
355    */

356   protected void finishInit() {
357     if (agentState < STATE_INIT_STARTED) {
358       logger.fatal("Agent initialization finish is called before "+
359                    "initialization, current state is "+agentState);
360     }
361     agent.setNotificationOriginator(notificationOriginator);
362     agent.addMOServer(server);
363     agent.setCoexistenceProvider(snmpCommunityMIB);
364     agent.setVacm(vacmMIB);
365     dispatcher.addCommandResponder(agent);
366     agentState = STATE_INIT_FINISHED;
367   }
368
369   protected void sendColdStartNotification() {
370     notificationOriginator.notify(new OctetString(), SnmpConstants.coldStart,
371                                   new VariableBinding[0]);
372   }
373
374   /**
375    * Starts the agent by let it listen on the configured SNMP agent ports
376    * (transpot mappings).
377    */

378   public void run() {
379     if (agentState < STATE_INIT_FINISHED) {
380       logger.fatal("Agent is run uninitialized, current state is "+agentState);
381     }
382     if (agentState == STATE_STOPPED) {
383       initSnmpSession();
384     }
385     try {
386       session.listen();
387     }
388     catch (IOException iox) {
389       logger.error(iox);
390     }
391   }
392
393   /**
394    * Stops the agent by closing the SNMP session and associated transport
395    * mappings.
396    * @since 1.1
397    */

398   public void stop() {
399     if (agentState != STATE_RUNNING) {
400       logger.error("Agent is stopped although it is not running, "+
401                    "current state is "+agentState);
402     }
403     try {
404       session.close();
405     }
406     catch (IOException ex) {
407       logger.warn("Closing agent session threw IOException: "+ex.getMessage());
408     }
409     session = null;
410     agentState = STATE_STOPPED;
411   }
412
413   /**
414    * Initializes the message dispatcher ({@link MessageDispatcherImpl}) with
415    * the transport mappings.
416    */

417   protected void initMessageDispatcher() {
418     dispatcher = new MessageDispatcherImpl();
419     mpv3 = new MPv3(agent.getContextEngineID().getValue());
420     usm = new USM(SecurityProtocols.getInstance(),
421                   agent.getContextEngineID(),
422                   updateEngineBoots());
423     SecurityModels.getInstance().addSecurityModel(usm);
424     SecurityProtocols.getInstance().addDefaultProtocols();
425     dispatcher.addMessageProcessingModel(new MPv1());
426     dispatcher.addMessageProcessingModel(new MPv2c());
427     dispatcher.addMessageProcessingModel(mpv3);
428     initSnmpSession();
429   }
430
431   protected void initSnmpSession() {
432     session = new Snmp(dispatcher);
433     for (int i=0; i<transportMappings.length; i++) {
434       session.addTransportMapping(transportMappings[i]);
435     }
436   }
437
438   /**
439    * Updates the engine boots counter and returns the actual value.
440    * @return
441    * the actual boots counter value.
442    */

443   protected int updateEngineBoots() {
444     int boots = getEngineBoots();
445     boots++;
446     if (boots <= 0) {
447       boots = 1;
448     }
449     setEngineBoots(boots);
450     return boots;
451   }
452
453   /**
454    * Reads the engine boots counter from the corresponding input stream (file).
455    * @return
456    * the boots counter value read or zero if it could not be read.
457    */

458   protected int getEngineBoots() {
459     FileInputStream fis = null;
460     try {
461       fis = new FileInputStream(bootCounterFile);
462       ObjectInputStream ois = new ObjectInputStream(fis);
463       int boots = ois.readInt();
464       if (logger.isInfoEnabled()) {
465         logger.info("Engine boots is: "+boots);
466       }
467       return boots;
468     }
469     catch (FileNotFoundException ex) {
470       logger.warn("Could not find boot counter file: "+bootCounterFile);
471     }
472     catch (IOException iox) {
473       if (logger.isDebugEnabled()) {
474         iox.printStackTrace();
475       }
476       logger.error("Failed to read boot counter: "+iox.getMessage());
477     }
478     finally {
479       if (fis != null) {
480         try {
481           fis.close();
482         }
483         catch (IOException ex1) {
484           logger.warn(ex1);
485         }
486       }
487     }
488     return 0;
489   }
490
491   protected void setEngineBoots(int engineBoots) {
492     FileOutputStream fos = null;
493     try {
494       fos = new FileOutputStream(bootCounterFile);
495       ObjectOutputStream oos = new ObjectOutputStream(fos);
496       oos.writeInt(engineBoots);
497       oos.close();
498       if (logger.isInfoEnabled()) {
499         logger.info("Wrote boot counter: " + engineBoots);
500       }
501     }
502     catch (FileNotFoundException fnfex) {
503       logger.error("Boot counter configuration file not found: "+
504                    fnfex.getMessage());
505     }
506     catch (IOException iox) {
507       logger.error("Failed to write boot counter: "+iox.getMessage());
508     }
509     finally {
510       if (fos != null) {
511         try {
512           fos.close();
513         }
514         catch (IOException ex1) {
515           logger.warn(ex1);
516         }
517       }
518     }
519   }
520
521   /**
522    * Adds all the necessary initial users to the USM.
523    * @param usm
524    * the USM instance used by this agent.
525    */

526   protected abstract void addUsmUser(USM usm);
527
528   /**
529    * Adds initial notification targets and filters.
530    * @param targetMIB
531    * the SnmpTargetMIB holding the target configuration.
532    * @param notificationMIB
533    * the SnmpNotificationMIB holding the notification (filter)
534    * configuration.
535    */

536   protected abstract void addNotificationTargets(SnmpTargetMIB targetMIB,
537                                                  SnmpNotificationMIB notificationMIB);
538
539   /**
540    * Adds initial VACM configuration.
541    * @param vacmMIB
542    * the VacmMIB holding the agent's view configuration.
543    */

544   protected abstract void addViews(VacmMIB vacmMIB);
545
546   /**
547    * Adds community to security name mappings needed for SNMPv1 and SNMPv2c.
548    * @param communityMIB
549    * the SnmpCommunityMIB holding coexistence configuration for community
550    * based security models.
551    */

552   protected abstract void addCommunities(SnmpCommunityMIB communityMIB);
553
554   /**
555    * Initializes the transport mappings (ports) to be used by the agent.
556    * @throws IOException
557    */

558   protected void initTransportMappings() throws IOException {
559     transportMappings = new TransportMapping[1];
560     transportMappings[0] =
561         new DefaultUdpTransportMapping(new UdpAddress("0.0.0.0/161"));
562   }
563
564   public NotificationOriginator getNotificationOriginator() {
565     return notificationOriginator;
566   }
567
568   public void setDefaultProxyForwarder(ProxyForwarder defaultProxyForwarder) {
569     this.defaultProxyForwarder = defaultProxyForwarder;
570   }
571
572   public void setSysDescr(OctetString sysDescr) {
573     this.sysDescr = sysDescr;
574   }
575
576   public void setSysOID(OID sysOID) {
577     this.sysOID = sysOID;
578   }
579
580   public void setSysServices(Integer32 sysServices) {
581     this.sysServices = sysServices;
582   }
583
584   public void setAgent(CommandProcessor agent) {
585     this.agent = agent;
586   }
587
588   public void setBootCounterFile(File bootCounterFile) {
589     this.bootCounterFile = bootCounterFile;
590   }
591
592   public void setConfigFile(File configFile) {
593     this.configFile = configFile;
594   }
595
596   /**
597    * Sets the default context for this base agent. By setting this value before
598    * any MIB modules have been registered at the internal server, the context
599    * for which the registration is performed can be changed. The default context
600    * is <code>null</code> which causes MIB objects to be virtually registered
601    * for all contexts.
602    *
603    * @param defaultContext
604    * the context for default MIB objects.
605    * @since 1.1
606    */

607   public void setDefaultContext(OctetString defaultContext) {
608     this.defaultContext = defaultContext;
609   }
610
611   public ProxyForwarder getDefaultProxyForwarder() {
612     return defaultProxyForwarder;
613   }
614
615   public OctetString getSysDescr() {
616     return sysDescr;
617   }
618
619   public OID getSysOID() {
620     return sysOID;
621   }
622
623   public Integer32 getSysServices() {
624     return sysServices;
625   }
626
627   public CommandProcessor getAgent() {
628     return agent;
629   }
630
631   public File getBootCounterFile() {
632     return bootCounterFile;
633   }
634
635   public File getConfigFile() {
636     return configFile;
637   }
638
639   public Snmp4jConfigMib getSnmp4jConfigMIB() {
640     return snmp4jConfigMIB;
641   }
642
643   public Snmp4jLogMib getSnmp4jLogMIB() {
644     return snmp4jLogMIB;
645   }
646
647   public SnmpCommunityMIB getSnmpCommunityMIB() {
648     return snmpCommunityMIB;
649   }
650
651   public SnmpFrameworkMIB getSnmpFrameworkMIB() {
652     return snmpFrameworkMIB;
653   }
654
655   public SnmpNotificationMIB getSnmpNotificationMIB() {
656     return snmpNotificationMIB;
657   }
658
659   public SnmpProxyMIB getSnmpProxyMIB() {
660     return snmpProxyMIB;
661   }
662
663   public SnmpTargetMIB getSnmpTargetMIB() {
664     return snmpTargetMIB;
665   }
666
667   public SNMPv2MIB getSnmpv2MIB() {
668     return snmpv2MIB;
669   }
670
671   public UsmMIB getUsmMIB() {
672     return usmMIB;
673   }
674
675   public VacmMIB getVacmMIB() {
676     return vacmMIB;
677   }
678
679   public Snmp getSession() {
680     return session;
681   }
682
683   public DefaultMOServer getServer() {
684     return server;
685   }
686
687   public MPv3 getMPv3() {
688     return mpv3;
689   }
690
691   public USM getUsm() {
692     return usm;
693   }
694
695   /**
696    * Returns the agent's state.
697    * @return
698    * one of the state's starting from {@link #STATE_CREATED} to
699    * {@link #STATE_RUNNING}.
700    * @since 1.1
701    */

702   public int getAgentState() {
703     return agentState;
704   }
705
706   /**
707    * Returns the default context - which is the context that is used by the
708    * base agent to register its MIB objects. By default it is <code>null</code>
709    * which causes the objects to be registered virtually for all contexts.
710    * In that case, subagents for example my not register their own objects
711    * under the same subtree(s) in any context. To allow subagents to register
712    * their own instances of those MIB modules, an empty <code>OctetString</code>
713    * should be used as default context instead.
714    * @return
715    * <code>null</code> or an <code>OctetString</code> (normally the empty
716    * string) denoting the context used for registering default MIBs.
717    * @since 1.1
718    */

719   public OctetString getDefaultContext() {
720     return defaultContext;
721   }
722 }
723
Popular Tags