View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.utility;
21  
22  import io.netty.bootstrap.Bootstrap;
23  import io.netty.bootstrap.ServerBootstrap;
24  import io.netty.buffer.ByteBuf;
25  import io.netty.buffer.PooledByteBufAllocator;
26  import io.netty.buffer.Unpooled;
27  import io.netty.channel.ChannelOption;
28  import io.netty.channel.EventLoopGroup;
29  import io.netty.channel.WriteBufferWaterMark;
30  import io.netty.channel.socket.nio.NioServerSocketChannel;
31  import io.netty.channel.socket.nio.NioSocketChannel;
32  import io.netty.util.IllegalReferenceCountException;
33  import io.netty.util.concurrent.Future;
34  import org.waarp.common.logging.SysErrLogger;
35  
36  import java.io.IOException;
37  import java.net.InetAddress;
38  import java.net.ServerSocket;
39  
40  /**
41   * Utility class for Netty usage
42   */
43  public final class WaarpNettyUtil {
44  
45    private static final int TIMEOUT_MILLIS = 1000;
46    /**
47     * Used in final operation to wait for extra works
48     */
49    public static final int SIMPLE_DELAY_MS = 100;
50    /**
51     * Minimal delay (OS sleep minimal time)
52     */
53    public static final int MINIMAL_DELAY_MS = 10;
54    // Default optimal value from Netty (tested as correct for Waarp)
55    private static final int DEFAULT_LOW_WATER_MARK = 32 * 1024;
56    private static final int DEFAULT_HIGH_WATER_MARK = 64 * 1024 + 64;
57    // Default optimal value for Waarp (in particular R66)
58    private static final int BUFFER_SIZE_1MB = DEFAULT_HIGH_WATER_MARK * 16;
59  
60    private WaarpNettyUtil() {
61    }
62  
63    private static int getSoBufSize(final int maxBufSize) {
64      int soBuf = Math.min(maxBufSize * 16, BUFFER_SIZE_1MB);
65      if (soBuf < maxBufSize * 4) {
66        soBuf = maxBufSize * 4;
67      }
68      return soBuf;
69    }
70  
71    /**
72     * Add default configuration for client bootstrap
73     *
74     * @param bootstrap
75     * @param group
76     * @param timeout
77     */
78    public static void setBootstrap(final Bootstrap bootstrap,
79                                    final EventLoopGroup group,
80                                    final int timeout) {
81      setBootstrap(bootstrap, group, timeout, DEFAULT_LOW_WATER_MARK, true);
82    }
83  
84    /**
85     * Add default configuration for client bootstrap
86     *
87     * @param bootstrap
88     * @param group
89     * @param timeout
90     * @param maxBufSize
91     * @param autoRead
92     */
93    public static void setBootstrap(final Bootstrap bootstrap,
94                                    final EventLoopGroup group, final int timeout,
95                                    final int maxBufSize,
96                                    final boolean autoRead) {
97      bootstrap.channel(NioSocketChannel.class);
98      bootstrap.group(group);
99      bootstrap.option(ChannelOption.TCP_NODELAY, false);
100     bootstrap.option(ChannelOption.SO_REUSEADDR, true);
101     bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
102     bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);
103     final int so_buf = getSoBufSize(maxBufSize);
104     final int lowWaterMark = Math.min(DEFAULT_LOW_WATER_MARK, maxBufSize / 2);
105     bootstrap.option(ChannelOption.SO_RCVBUF, so_buf);
106     bootstrap.option(ChannelOption.SO_SNDBUF, so_buf);
107     bootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK,
108                      new WriteBufferWaterMark(lowWaterMark, maxBufSize));
109     bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
110     if (!autoRead) {
111       bootstrap.option(ChannelOption.AUTO_READ, false);
112     }
113   }
114 
115   /**
116    * Add default configuration for server bootstrap
117    *
118    * @param bootstrap
119    * @param parentGroup
120    * @param childGroup
121    * @param timeout
122    */
123   public static void setServerBootstrap(final ServerBootstrap bootstrap,
124                                         final EventLoopGroup parentGroup,
125                                         final EventLoopGroup childGroup,
126                                         final int timeout) {
127     setServerBootstrap(bootstrap, parentGroup, childGroup, timeout,
128                        DEFAULT_LOW_WATER_MARK, true);
129   }
130 
131   /**
132    * Add default configuration for server bootstrap
133    *
134    * @param bootstrap
135    * @param parentGroup
136    * @param childGroup
137    * @param timeout
138    * @param maxBufSize
139    * @param autoRead
140    */
141   public static void setServerBootstrap(final ServerBootstrap bootstrap,
142                                         final EventLoopGroup parentGroup,
143                                         final EventLoopGroup childGroup,
144                                         final int timeout, final int maxBufSize,
145                                         final boolean autoRead) {
146     bootstrap.channel(NioServerSocketChannel.class);
147     bootstrap.group(parentGroup, childGroup);
148     // bootstrap.option(ChannelOption.TCP_NODELAY, true)
149     bootstrap.option(ChannelOption.SO_REUSEADDR, true);
150     bootstrap.childOption(ChannelOption.TCP_NODELAY, false);
151     bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
152     bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
153     bootstrap.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);
154     final int so_buf = getSoBufSize(maxBufSize);
155     final int lowWaterMark = Math.min(DEFAULT_LOW_WATER_MARK, maxBufSize / 2);
156     bootstrap.childOption(ChannelOption.SO_RCVBUF, so_buf);
157     bootstrap.childOption(ChannelOption.SO_SNDBUF, so_buf);
158     bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
159                           new WriteBufferWaterMark(lowWaterMark, maxBufSize));
160     bootstrap.childOption(ChannelOption.ALLOCATOR,
161                           PooledByteBufAllocator.DEFAULT);
162     if (!autoRead) {
163       bootstrap.childOption(ChannelOption.AUTO_READ, false);
164     }
165   }
166 
167   /**
168    * Checks to see if a specific port is available.
169    *
170    * @param port the port to check for availability
171    */
172   public static boolean availablePort(final int port) {
173     ServerSocket ss = null;
174     try {
175       ss = new ServerSocket(port); // NOSONAR
176       ss.setReuseAddress(true);
177       return true;
178     } catch (final IOException e) {
179       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
180     } finally {
181       if (ss != null) {
182         try {
183           ss.close();
184         } catch (final Exception e) {
185           SysErrLogger.FAKE_LOGGER.ignoreLog(e);
186         }
187       }
188     }
189     return false;
190   }
191 
192   /**
193    * Checks to see if a specific port is available.
194    *
195    * @param port the port to check for availability
196    * @param localAddress the associated localAddress to use
197    */
198   public static boolean availablePort(final int port,
199                                       final InetAddress localAddress) {
200     ServerSocket ss = null;
201     try {
202       ss = new ServerSocket(port, 0, localAddress); // NOSONAR
203       ss.setReuseAddress(true);
204       return true;
205     } catch (final IOException e) {
206       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
207     } finally {
208       if (ss != null) {
209         try {
210           ss.close();
211         } catch (final Exception e) {
212           SysErrLogger.FAKE_LOGGER.ignoreLog(e);
213         }
214       }
215     }
216     return false;
217   }
218 
219   /**
220    * @param future
221    *
222    * @return True if await done, else interruption occurs
223    */
224   public static boolean awaitOrInterrupted(final Future<?> future) {
225     try {
226       while (!Thread.interrupted()) {
227         if (future.await(TIMEOUT_MILLIS)) {
228           return true;
229         }
230       }
231     } catch (final InterruptedException e) {//NOSONAR
232       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
233     }
234     return false;
235   }
236 
237   /**
238    * @param future
239    * @param timeMilliseconds
240    *
241    * @return True if await done, else interruption occurs
242    */
243   public static boolean awaitOrInterrupted(final Future<?> future,
244                                            final long timeMilliseconds) {
245     try {
246       if (future.await(timeMilliseconds)) {
247         return !Thread.interrupted();
248       }
249     } catch (final InterruptedException e) {//NOSONAR
250       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
251     }
252     return false;
253   }
254 
255   /**
256    * @param future
257    *
258    * @return True if await and isSuccess, else interruption or not success
259    *     occurs
260    */
261   public static boolean awaitIsSuccessOfInterrupted(final Future<?> future) {
262     try {
263       while (!Thread.interrupted()) {
264         if (future.await(TIMEOUT_MILLIS)) {
265           return future.isSuccess();
266         }
267       }
268     } catch (final InterruptedException e) {//NOSONAR
269       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
270     }
271     return false;
272   }
273 
274   /**
275    * Shortcut to release a ByteByf
276    *
277    * @param byteBuf
278    *
279    * @return True if the ByteBuf is released
280    */
281   public static boolean release(final ByteBuf byteBuf) {
282     if (byteBuf == null || byteBuf.refCnt() <= 0) {
283       return true;
284     }
285     try {
286       return byteBuf.release();
287     } catch (final IllegalReferenceCountException e) {
288       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
289       return true;
290     }
291   }
292 
293   /**
294    * Shortcut to release completely a ByteByf
295    *
296    * @param byteBuf
297    */
298   public static void releaseCompletely(final ByteBuf byteBuf) {
299     if (byteBuf != null && byteBuf.refCnt() != 0) {
300       final int refCnt = byteBuf.refCnt();
301       try {
302         byteBuf.release(refCnt);
303       } catch (final IllegalReferenceCountException e) {
304         SysErrLogger.FAKE_LOGGER.ignoreLog(e);
305       }
306     }
307   }
308 
309   /**
310    * Retain this ByteBuf
311    *
312    * @param byteBuf
313    */
314   public static void retain(final ByteBuf byteBuf) {
315     if (byteBuf != null) {
316       try {
317         byteBuf.retain();
318       } catch (final IllegalReferenceCountException e) {
319         SysErrLogger.FAKE_LOGGER.ignoreLog(e);
320       }
321     }
322   }
323 
324   /**
325    * Utility method to return a ByteBuf slice with Write ready.
326    * No retain is called on this slice.
327    *
328    * @param byteBuf
329    * @param start
330    * @param size
331    *
332    * @return the ByteBuf sliced
333    */
334   public static ByteBuf slice(final ByteBuf byteBuf, final int start,
335                               final int size) {
336     final ByteBuf bufSliced = byteBuf.slice(start, size);
337     bufSliced.writerIndex(0);
338     return bufSliced;
339   }
340 
341   /**
342    * Use the best approach
343    *
344    * @param arrays
345    *
346    * @return the ByteBuf corresponding to byte arrays
347    */
348   public static ByteBuf wrappedBuffer(final byte[]... arrays) {
349     return Unpooled.wrappedBuffer(arrays);
350   }
351 }