001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.snmp;
018    
019    import javax.jbi.management.DeploymentException;
020    import javax.jbi.messaging.InOnly;
021    import javax.jbi.messaging.MessageExchange;
022    import javax.jbi.messaging.MessagingException;
023    import javax.jbi.messaging.NormalizedMessage;
024    
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    import org.apache.servicemix.common.endpoints.PollingEndpoint;
028    import org.apache.servicemix.snmp.marshaler.DefaultSnmpMarshaler;
029    import org.apache.servicemix.snmp.marshaler.SnmpMarshalerSupport;
030    import org.apache.servicemix.snmp.util.OIDList;
031    import org.snmp4j.CommunityTarget;
032    import org.snmp4j.PDU;
033    import org.snmp4j.Snmp;
034    import org.snmp4j.TransportMapping;
035    import org.snmp4j.event.ResponseEvent;
036    import org.snmp4j.event.ResponseListener;
037    import org.snmp4j.mp.MPv3;
038    import org.snmp4j.mp.SnmpConstants;
039    import org.snmp4j.security.SecurityModels;
040    import org.snmp4j.security.SecurityProtocols;
041    import org.snmp4j.security.USM;
042    import org.snmp4j.smi.Address;
043    import org.snmp4j.smi.GenericAddress;
044    import org.snmp4j.smi.OID;
045    import org.snmp4j.smi.OctetString;
046    import org.snmp4j.smi.VariableBinding;
047    import org.snmp4j.transport.DefaultUdpTransportMapping;
048    
049    /**
050     * This is the polling endpoint for the snmp component.
051     * 
052     * @org.apache.xbean.XBean element="poller"
053     * @author lhein
054     */
055    public class SnmpPollingEndpoint extends PollingEndpoint implements SnmpEndpointType, ResponseListener {
056        private static final transient Log LOG = LogFactory.getLog(SnmpPollingEndpoint.class);
057    
058        public static final String DEFAULT_COMMUNITY = "public";
059        public static final int DEFAULT_SNMP_VERSION = SnmpConstants.version1;
060        public static final int DEFAULT_SNMP_RETRIES = 2;
061        public static final int DEFAULT_SNMP_TIMEOUT = 1500;
062    
063        private Address targetAddress;
064        private TransportMapping transport;
065        private Snmp snmp;
066        private USM usm;
067        private CommunityTarget target;
068        private PDU pdu;
069    
070        private OIDList oids = new OIDList();
071        private String address;
072        private int retries = DEFAULT_SNMP_RETRIES;
073        private int timeout = DEFAULT_SNMP_TIMEOUT;
074        private int snmpVersion = DEFAULT_SNMP_VERSION;
075        private String snmpCommunity = DEFAULT_COMMUNITY;
076    
077        private SnmpMarshalerSupport marshaler = new DefaultSnmpMarshaler();
078    
079        /*
080         * (non-Javadoc)
081         * @see org.apache.servicemix.common.endpoints.ConsumerEndpoint#activate()
082         */
083        @Override
084        public synchronized void activate() throws Exception {
085            super.activate();
086    
087            this.targetAddress = GenericAddress.parse(this.address);
088            this.transport = new DefaultUdpTransportMapping();
089            this.snmp = new Snmp(transport);
090            this.usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
091            SecurityModels.getInstance().addSecurityModel(usm);
092    
093            // setting up target
094            target = new CommunityTarget();
095            target.setCommunity(new OctetString(this.snmpCommunity));
096            target.setAddress(targetAddress);
097            target.setRetries(this.retries);
098            target.setTimeout(this.timeout);
099            target.setVersion(this.snmpVersion);
100    
101            // creating PDU
102            this.pdu = new PDU();
103        }
104    
105        /*
106         * (non-Javadoc)
107         * @see org.apache.servicemix.common.endpoints.PollingEndpoint#start()
108         */
109        @Override
110        public synchronized void start() throws Exception {
111            super.start();
112    
113            // again listen to the transport
114            this.transport.listen();
115        }
116    
117        /*
118         * (non-Javadoc)
119         * @see org.apache.servicemix.common.endpoints.PollingEndpoint#stop()
120         */
121        @Override
122        public synchronized void stop() throws Exception {
123            // stop listening to the transport
124            if (this.transport.isListening()) {
125                this.transport.close();
126            }
127    
128            super.stop();
129        }
130    
131        /*
132         * (non-Javadoc)
133         * @see org.apache.servicemix.common.endpoints.ConsumerEndpoint#validate()
134         */
135        @Override
136        public void validate() throws DeploymentException {
137            super.validate();
138    
139            // check address not null
140            if (this.address == null) {
141                throw new DeploymentException("The address attribute has to be specified!");
142            }
143    
144            // check if address is valid
145            try {
146                if (GenericAddress.parse(this.address) == null) {
147                    throw new DeploymentException("The specified address " + address + " is not valid!");
148                }
149            } catch (IllegalArgumentException ex) {
150                throw new DeploymentException("The specified address " + address + " is not valid!");
151            }
152            
153            // finally check if the oid vector contains values
154            if (this.oids == null || this.oids.size()<=0) {
155                // the poller would be unemployed
156                throw new DeploymentException("There are no OIDs defined to be polled. Check your oids attribute.");
157            }
158        }
159    
160        /*
161         * (non-Javadoc)
162         * @see org.apache.servicemix.common.endpoints.PollingEndpoint#poll()
163         */
164        @Override
165        public void poll() throws Exception {
166            this.pdu.clear();
167            this.pdu.setType(PDU.GET);
168    
169            // prepare the request items
170            for (OID oid : oids) {
171                this.pdu.add(new VariableBinding(oid));
172            }
173    
174            // send the request
175            snmp.send(pdu, target, null, this);
176        }
177    
178        /*
179         * (non-Javadoc)
180         * @see
181         * org.apache.servicemix.common.endpoints.AbstractEndpoint#process(javax
182         * .jbi.messaging.MessageExchange)
183         */
184        @Override
185        public void process(MessageExchange exchange) throws Exception {
186            // only DONE and ERROR states will be received here and this
187            // endpoint is not interested in such messages at all
188        }
189    
190        /*
191         * (non-Javadoc)
192         * @see
193         * org.snmp4j.event.ResponseListener#onResponse(org.snmp4j.event.ResponseEvent
194         * )
195         */
196        public void onResponse(ResponseEvent event) {
197            // Always cancel async request when response has been received
198            // otherwise a memory leak is created! Not canceling a request
199            // immediately can be useful when sending a request to a broadcast
200            // address.
201            ((Snmp)event.getSource()).cancel(event.getRequest(), this);
202    
203            // check for valid response
204            if (event.getRequest() == null || event.getResponse() == null) {
205                // ignore null requests/responses
206                LOG.debug("Received invalid snmp event. Request: " + event.getRequest() + " / Response: "
207                          + event.getResponse());
208                return;
209            }
210    
211            // now prepare the message and send it
212            sendSnmpDataMessage(event.getRequest(), event.getResponse());
213        }
214    
215        /**
216         * sends the message to the bus
217         * 
218         * @param request the request PDU
219         * @param response the response PDU
220         */
221        private void sendSnmpDataMessage(PDU request, PDU response) {
222            try {
223                // create a inOnly exchange
224                InOnly io = getExchangeFactory().createInOnlyExchange();
225    
226                // configure the exchange target
227                configureExchangeTarget(io);
228    
229                // create the in message
230                NormalizedMessage inMsg = io.createMessage();
231    
232                // now let the marshaller convert the snmp data into a normalized
233                // message to send to jbi bus
234                this.marshaler.convertToJBI(io, inMsg, request, response);
235    
236                // then put the in message into the inOnly exchange
237                io.setInMessage(inMsg);
238    
239                // and use send to deliver it
240                getChannel().send(io);
241            } catch (MessagingException ex) {
242                LOG.error("Error while trying to send the snmp event to the jbi bus", ex);
243            }
244        }
245    
246        public String getAddress() {
247            return this.address;
248        }
249    
250        /**
251         * <p>Specifies the connection URI used to connect to a snmp capable device.
252         * <br /><br />
253         * <b><u>Template:</u></b> <br />
254         *     &nbsp;&nbsp;&nbsp;<i>&lt;protocol&gt;:&lt;host&gt;/&lt;port&gt;</i>
255         * <br /><br />
256         * <b><u>Details:</u></b><br /><br/>
257         * <table border="0" cellpadding="0" cellspacing="0">
258         * <tr>
259         *          <td width="40%" align="left"><b><u>Name</u></b></td>
260         *          <td width="60%" align="left"><b><u>Description</u></b></td>
261         * </tr>
262         * <tr>
263         *          <td>protocol</td>
264         *          <td>the protocol to use (udp or tcp)</td>
265         * </tr>
266         * <tr>
267         *          <td>host</td>
268         *          <td>the name or ip address of the snmp capable device</td>
269         * </tr>
270         * <tr>
271         *          <td>port</td>
272         *          <td>the port number to use</td>
273         * </tr>
274         * </table>
275         * <br/>
276         * <b><u>Example:</u></b><br />
277         * &nbsp;&nbsp;&nbsp;<i>udp:192.168.2.122/161</i></p>
278         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>null</b></i><br/><br/>
279         * 
280         * @param address 
281         *                          a <code>String</code> value containing the connection details
282         */
283        public void setAddress(String address) {
284            this.address = address;
285        }
286    
287        public int getRetries() {
288            return this.retries;
289        }
290    
291        /**
292         * <p>Specifies the connection retries.</p>
293         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>2</b></i><br/><br/>
294         * 
295         * @param retries 
296         *                          a <code>int</code> value containing the retry count
297         */
298        public void setRetries(int retries) {
299            this.retries = retries;
300        }
301    
302        public int getTimeout() {
303            return this.timeout;
304        }
305    
306        /**
307         * <p>Specifies the connection time out in milliseconds.</p>
308         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>1500</b></i><br/><br/>
309         * 
310         * @param timeout 
311         *                          a <code>int</code> value containing the time out in millis
312         */
313        public void setTimeout(int timeout) {
314            this.timeout = timeout;
315        }
316    
317        public int getSnmpVersion() {
318            return this.snmpVersion;
319        }
320    
321        /**
322         * <p>Specifies the snmp protocol version to use.</p>
323         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>0 (version 1)</b></i><br/><br/>
324         * 
325         * @param snmpVersion 
326         *                          a <code>int</code> value containing the snmp version
327         */
328        public void setSnmpVersion(int snmpVersion) {
329            this.snmpVersion = snmpVersion;
330        }
331    
332        public String getSnmpCommunity() {
333            return this.snmpCommunity;
334        }
335    
336        /**
337         * <p>Specifies the snmp community to use.</p>
338         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>"public"</b></i><br/><br/>
339         * 
340         * @param snmpCommunity 
341         *                          a <code>String</code> value containing the snmp community
342         */
343        public void setSnmpCommunity(String snmpCommunity) {
344            this.snmpCommunity = snmpCommunity;
345        }
346    
347        public SnmpMarshalerSupport getMarshaler() {
348            return this.marshaler;
349        }
350    
351        /**
352         * <p>Specifies a marshaler class which provides the logic for converting 
353         * a snmp response into a normalized message. This class has to implement 
354         * the <code>SnmpMarshalerSupport</code> interface. If you don't specify a 
355         * marshaler, the <code>DefaultSnmpMarshaler</code> will be used.</p>
356         * 
357         * @param marshaler 
358         *                          a class which implements <code>SnmpMarshalerSupport</code>
359         */
360        public void setMarshaler(SnmpMarshalerSupport marshaler) {
361            this.marshaler = marshaler;
362        }
363    
364        public OIDList getOids() {
365            return this.oids;
366        }
367    
368        /**
369         * <p>Specifies a reference to a list of OID values which will be used for 
370         * the snmp request. You have two possibilities how to specify the value:
371         * <br /><br />
372         * &nbsp;<i>a) referencing to a file containing a list of OID values separated by a line feed
373         * <br/>&nbsp;&nbsp;&nbsp;&nbsp;or<br/>
374         * &nbsp;<i>b) defining a coma (<b>,</b>) separated list of OID values 
375         * <br /><br />
376         * <b><u>Examples:</u></b><br />
377         * &nbsp;&nbsp;&nbsp;<i>a) oids="classpath:myOids.txt"<br />
378         * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oids="file:/home/lhein/snmp/device_a/oids.txt"<br/>
379         * <br />
380         * &nbsp;&nbsp;&nbsp;<i>b) oids="1.3.6.1.2.1.1.3.0 , 1.3.6.1.2.1.25.3.2.1.5.1 , 1.3.6.1.2.1.25.3.5.1.1.1 , 1.3.6.1.2.1.43.5.1.1.11.1"</i></p>
381         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>null</b></i><br/><br/>
382         * 
383         * @param oids 
384         *                          a <code>OIDList</code> containing the OID values for the request
385         */
386        public void setOids(OIDList oids) {
387            this.oids = oids;
388        }
389    }