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.servicemix.common.endpoints.ConsumerEndpoint;
026    import org.apache.servicemix.snmp.marshaler.DefaultSnmpMarshaler;
027    import org.apache.servicemix.snmp.marshaler.SnmpMarshalerSupport;
028    import org.snmp4j.CommandResponder;
029    import org.snmp4j.CommandResponderEvent;
030    import org.snmp4j.PDU;
031    import org.snmp4j.Snmp;
032    import org.snmp4j.TransportMapping;
033    import org.snmp4j.smi.Address;
034    import org.snmp4j.smi.GenericAddress;
035    import org.snmp4j.smi.UdpAddress;
036    import org.snmp4j.transport.DefaultUdpTransportMapping;
037    
038    /**
039     * This is the trap endpoint for the snmp component.
040     * 
041     * This endpoint receives and process trap PDUs from
042     * a device. Then it sends an exchange to the target service 
043     * containing the processed content.
044     * 
045     * @org.apache.xbean.XBean element="trap-consumer"
046     * @author gperdiguero
047     * @author jbonofre
048     */
049    public class SnmpTrapConsumerEndpoint extends ConsumerEndpoint implements SnmpEndpointType, CommandResponder {
050        
051        public static final boolean DEFAULT_ENABLED_VALUE = true;
052        
053        private Address listenGenericAddress;
054        private Snmp snmp;
055        private TransportMapping transport;
056    
057        private String address;
058        private boolean enabled = DEFAULT_ENABLED_VALUE;
059        
060        private SnmpMarshalerSupport marshaler = new DefaultSnmpMarshaler();
061    
062        /*
063         * (non-Javadoc)
064         * @see org.apache.servicemix.common.endpoints.ConsumerEndpoint#activate()
065         */
066        @Override
067        public synchronized void activate() throws Exception {
068            super.activate();
069            // load connection data only if the endpoint is enabled
070            if (isEnabled()) {
071                logger.debug("Activating endpoint");
072                this.listenGenericAddress = GenericAddress.parse(this.address);
073                this.transport = new DefaultUdpTransportMapping((UdpAddress) this.listenGenericAddress);
074                this.snmp = new Snmp(transport);
075                snmp.addCommandResponder(this);
076            }
077        }
078        
079        /*
080         * (non-Javadoc)
081         * @see org.apache.servicemix.common.endpoints.PollingEndpoint#start()
082         */
083        @Override
084        public synchronized void start() throws Exception {
085            super.start();
086            
087            // listening is only allowed if the endpoint was initialized
088            if (isEnabled()) {
089                // listen to the transport
090                this.transport.listen();
091            }
092        }
093        
094        /*
095         * (non-Javadoc)
096         * @see org.apache.servicemix.common.endpoints.PollingEndpoint#stop()
097         */
098        @Override
099        public synchronized void stop() throws Exception {
100            // stop listening only if the endpoint was initialized
101            if (isEnabled()) {
102                // stop listening to the transport
103                if (this.transport.isListening()) {
104                    this.transport.close();
105                }
106            }
107                
108            super.stop();
109        }
110        
111        /*
112         * (non-Javadoc)
113         * @see org.apache.servicemix.common.endpoints.ConsumerEndpoint#validate()
114         */
115        @Override
116        public void validate() throws DeploymentException {
117            super.validate();
118            
119            // check listen address not null
120            if (this.address == null) {
121                throw new DeploymentException("The listen address attribute has to be specified!");
122            }
123            
124            // check if address is valid
125            try {
126                if (GenericAddress.parse(this.address) == null) {
127                    throw new DeploymentException("The specified address " + address + " is not valid!");
128                }
129            } catch (IllegalArgumentException ex) {
130                throw new DeploymentException("The specified address " + address + " is not valid!");
131            }
132        }
133        
134        /*
135         * (non-Javadoc)
136         * @see
137         * org.apache.servicemix.common.endpoints.AbstractEndpoint#process(javax
138         * .jbi.messaging.MessageExchange)
139         */
140        @Override
141        public void process(MessageExchange exchange) throws Exception {
142            // only DONE and ERROR states will be received here and this
143            // endpoint is not interested in such messages at all
144        }
145    
146        /*
147         * (non-Javadoc)
148         * @see
149         * org.snmp4j.CommandResponder#processPdu(org.snmp4j.CommandResponderEvent) 
150         */
151        public void processPdu(CommandResponderEvent event) {
152            PDU pdu = event.getPDU();
153            // check PDU not null
154            if (pdu != null) {
155                sendSnmpTrapMessage(pdu);
156            } else {
157                logger.debug("Received invalid trap PDU: " + pdu);
158            }
159        }
160    
161        /**
162         * Sends the message to the bus 
163         * @param pdu the trap received
164         */
165        private void sendSnmpTrapMessage(PDU pdu) {
166            try {
167                // create an inOnly exchange
168                InOnly io = getExchangeFactory().createInOnlyExchange();
169                
170                // configure the exchange target
171                configureExchangeTarget(io);
172                
173                // create the in message
174                NormalizedMessage inMsg = io.createMessage();
175                
176                // now let the marshaler convert the snmp data into 
177                // a normalized message to send to jbi bus
178                this.marshaler.convertToJBI(io, inMsg, null, pdu);
179                
180                // put the in message into the inOnly exchange
181                io.setInMessage(inMsg);
182                
183                // send the exchange
184                getChannel().send(io);
185            } catch (MessagingException ex) {
186                logger.error("Error while trying to send the snmp trap PDU to the jbi bus", ex);
187            }
188        }
189    
190        public Snmp getSnmp() {
191            return this.snmp;
192        }
193    
194        public String getAddress() {
195            return this.address;
196        }
197    
198        /**
199         * <p>Specifies the connection URI used to connect to a snmp capable device.
200         * <br /><br />
201         * <b><u>Template:</u></b> <br />
202         *     &nbsp;&nbsp;&nbsp;<i>&lt;protocol&gt;:&lt;host&gt;/&lt;port&gt;</i>
203         * <br /><br />
204         * <b><u>Details:</u></b><br /><br/>
205         * <table border="0" cellpadding="0" cellspacing="0">
206         * <tr>
207         *      <td width="40%" align="left"><b><u>Name</u></b></td>
208         *      <td width="60%" align="left"><b><u>Description</u></b></td>
209         * </tr>
210         * <tr>
211         *      <td>protocol</td>
212         *      <td>the protocol to use (udp or tcp)</td>
213         * </tr>
214         * <tr>
215         *      <td>host</td>
216         *      <td>the name or ip address of the snmp capable device</td>
217         * </tr>
218         * <tr>
219         *      <td>port</td>
220         *      <td>the port number to use</td>
221         * </tr>
222         * </table>
223         * <br/>
224         * <b><u>Example:</u></b><br />
225         * &nbsp;&nbsp;&nbsp;<i>udp:192.168.2.122/162</i></p>
226         * <i>&nbsp;&nbsp;&nbsp;The default value is <b>null</b></i><br/><br/>
227         * 
228         * @param listenAddress 
229         *              a <code>String</code> value containing the connection details
230         */
231        public void setAddress(String address) {
232            this.address = address;
233        }
234    
235        public SnmpMarshalerSupport getMarshaler() {
236            return this.marshaler;
237        }
238    
239        /**
240         * <p>Specifies a marshaler class which provides the logic for converting 
241         * a snmp trap into a normalized message. This class has to implement 
242         * the <code>SnmpMarshalerSupport</code> interface. If you don't specify a 
243         * marshaler, the <code>DefaultSnmpMarshaler</code> will be used.</p>
244         * 
245         * @param marshaler 
246         *              a class which implements <code>SnmpMarshalerSupport</code>
247         */
248        public void setMarshaler(SnmpMarshalerSupport marshaler) {
249            this.marshaler = marshaler;
250        }
251    
252        public boolean isEnabled() {
253            return enabled;
254        }
255    
256        /**
257         * <p>Specifies wether the endpoint is enabled or not.
258         * If its value is set to true, the connection data will be
259         * setted and trap PDUs will be processed. Otherwise, 
260         * the endpoint won't do anything.</p> 
261         * @param enabled the enabled to set
262         */
263        public void setEnabled(boolean enabled) {
264            this.enabled = enabled;
265        }
266    }