View Javadoc
1   package org.metricshub.ipmi.core.transport;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * IPMI Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 Verax Systems, MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import java.io.IOException;
29  import java.net.DatagramPacket;
30  import java.net.DatagramSocket;
31  import java.net.InetAddress;
32  import java.net.SocketException;
33  import java.net.UnknownHostException;
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  /**
38   * Handles the UDP connection.
39   */
40  public class UdpMessenger extends Thread implements Messenger {
41  
42      private int port;
43  
44      private DatagramSocket socket;
45  
46      private List<UdpListener> listeners;
47  
48      private boolean closing = false;
49  
50      private static final String DEFAULT_ADDRESS = "0.0.0.0";
51  
52      /**
53       * Size of the message data buffer. Default
54       * {@link UdpMessenger#DEFAULTBUFFERSIZE}.
55       */
56      private int bufferSize;
57  
58      private static final int DEFAULTBUFFERSIZE = 512;
59  
60      private static Logger logger = LoggerFactory.getLogger(UdpMessenger.class);
61  
62      public int getPort() {
63          return port;
64      }
65  
66      /**
67       * Initiates UdpMessenger, binds it to the specified port and starts
68       * listening. Wildcard IP address will be used.
69       *
70       * @param port
71       *            - port to bind socket to.
72       * @throws SocketException
73       *             if the socket could not be opened, or the socket could not
74       *             bind to the specified local port.
75       * @throws UnknownHostException
76       */
77      public UdpMessenger(int port) throws SocketException, UnknownHostException {
78          this(port, InetAddress.getByName(DEFAULT_ADDRESS));
79      }
80  
81      /**
82       * Initiates UdpMessenger, binds it to the specified port and IP address and
83       * starts listening.
84       *
85       * @param port
86       *            - port to bind socket to.
87       * @param address
88       *            - IP address to bind socket to.
89       * @throws SocketException
90       *             if the socket could not be opened, or the socket could not
91       *             bind to the specified local port.
92       */
93      public UdpMessenger(int port, InetAddress address) throws SocketException {
94          sentPackets = 0;
95          this.port = port;
96          listeners = new ArrayList<UdpListener>();
97          bufferSize = DEFAULTBUFFERSIZE;
98          socket = new DatagramSocket(this.port, address);
99          socket.setSoTimeout(0);
100         this.start();
101     }
102 
103     /**
104      * Sets message data buffer size to bufferSize.
105      */
106     public void setBufferSize(int bufferSize) {
107         this.bufferSize = bufferSize;
108     }
109 
110     /**
111      * @return Size of the message data buffer
112      */
113     public int getBufferSize() {
114         return bufferSize;
115     }
116 
117     @Override
118     public void run() {
119         super.run();
120 
121         boolean run = true;
122 
123         while (run) {
124             DatagramPacket response = new DatagramPacket(new byte[512], 512);
125 
126             try {
127                 socket.receive(response);
128                 UdpMessage message = new UdpMessage();
129                 message.setAddress(response.getAddress());
130                 message.setPort(response.getPort());
131                 byte[] buffer = new byte[response.getLength()];
132                 System.arraycopy(response.getData(), 0, buffer, 0,
133                         buffer.length);
134                 message.setMessage(buffer);
135 
136                 notifyListeners(message);
137 
138             } catch (SocketException se) {
139                 if (closing) {
140                     run = false;
141                 } else {
142                     logger.error(se.getMessage(), se);
143                 }
144             } catch (Exception e) {
145                 logger.error(e.getMessage(), e);
146             } finally {
147                 if (socket.isClosed()) {
148                     run = false;
149                 }
150             }
151         }
152     }
153 
154     private void notifyListeners(UdpMessage message) {
155         synchronized (listeners) {
156             for (UdpListener listener : listeners) {
157                 if (listener != null) {
158                     listener.notifyMessage(message);
159                 }
160             }
161         }
162     }
163 
164     /**
165      * Closes the socket and releases port.
166      */
167     public void closeConnection() {
168         closing = true;
169         socket.close();
170     }
171 
172     /**
173      * Registers listener in the UdpMessenger so it will be notified via
174      * {@link UdpListener#notifyMessage(UdpMessage)} when new message arrives.
175      *
176      * @param listener
177      *            - {@link UdpListener} to register.
178      */
179     public void register(UdpListener listener) {
180         synchronized (listeners) {
181             listeners.add(listener);
182         }
183     }
184 
185     /**
186      * Unregisters listener from UdpMessenger so it no longer will be notified.
187      *
188      * @param listener
189      *            - {@link UdpListener} to unregister
190      */
191     public void unregister(UdpListener listener) {
192         synchronized (listeners) {
193             listeners.remove(listener);
194         }
195     }
196 
197     private static int sentPackets = 0;
198 
199     /**
200      * Returns number of packets sent since last creation of the instance of
201      * {@link UdpMessenger}. For debug/testing purposes only.
202      */
203     public static int getSentPackets() {
204         return sentPackets;
205     }
206 
207     /**
208      * Sends {@link UdpMessage}.
209      *
210      * @param message
211      *            - {@link UdpMessage} to send.
212      * @throws IOException
213      *             when sending of the message fails
214      */
215     public synchronized void send(UdpMessage message) throws IOException {
216         DatagramPacket packet = new DatagramPacket(message.getMessage(),
217                 message.getMessage().length, message.getAddress(),
218                 message.getPort());
219         socket.send(packet);
220         try {
221             Thread.sleep(1);
222         } catch (InterruptedException e) {
223             // TODO: log
224         }
225         ++sentPackets;
226     }
227 }