View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel;
17  
18  import io.netty.buffer.ByteBufAllocator;
19  import io.netty.channel.nio.AbstractNioByteChannel;
20  import io.netty.channel.socket.SocketChannelConfig;
21  import io.netty.util.internal.PlatformDependent;
22  
23  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
24  import java.util.IdentityHashMap;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  
28  import static io.netty.channel.ChannelOption.*;
29  
30  /**
31   * The default {@link SocketChannelConfig} implementation.
32   */
33  public class DefaultChannelConfig implements ChannelConfig {
34  
35      private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = AdaptiveRecvByteBufAllocator.DEFAULT;
36      private static final MessageSizeEstimator DEFAULT_MSG_SIZE_ESTIMATOR = DefaultMessageSizeEstimator.DEFAULT;
37  
38      private static final int DEFAULT_CONNECT_TIMEOUT = 30000;
39  
40      private static final AtomicIntegerFieldUpdater<DefaultChannelConfig> AUTOREAD_UPDATER;
41  
42      static {
43          AtomicIntegerFieldUpdater<DefaultChannelConfig> autoReadUpdater =
44              PlatformDependent.newAtomicIntegerFieldUpdater(DefaultChannelConfig.class, "autoRead");
45          if (autoReadUpdater == null) {
46              autoReadUpdater = AtomicIntegerFieldUpdater.newUpdater(DefaultChannelConfig.class, "autoRead");
47          }
48          AUTOREAD_UPDATER = autoReadUpdater;
49      }
50  
51      protected final Channel channel;
52  
53      private volatile ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
54      private volatile RecvByteBufAllocator rcvBufAllocator = DEFAULT_RCVBUF_ALLOCATOR;
55      private volatile MessageSizeEstimator msgSizeEstimator = DEFAULT_MSG_SIZE_ESTIMATOR;
56  
57      private volatile int connectTimeoutMillis = DEFAULT_CONNECT_TIMEOUT;
58      private volatile int maxMessagesPerRead;
59      private volatile int writeSpinCount = 16;
60      private volatile int autoRead = 1;
61      private volatile int writeBufferHighWaterMark = 64 * 1024;
62      private volatile int writeBufferLowWaterMark = 32 * 1024;
63  
64      public DefaultChannelConfig(Channel channel) {
65          if (channel == null) {
66              throw new NullPointerException("channel");
67          }
68          this.channel = channel;
69  
70          if (channel instanceof ServerChannel || channel instanceof AbstractNioByteChannel) {
71              // Server channels: Accept as many incoming connections as possible.
72              // NIO byte channels: Implemented to reduce unnecessary system calls even if it's > 1.
73              //                    See https://github.com/netty/netty/issues/2079
74              // TODO: Add some property to ChannelMetadata so we can remove the ugly instanceof
75              maxMessagesPerRead = 16;
76          } else {
77              maxMessagesPerRead = 1;
78          }
79      }
80  
81      @Override
82      public Map<ChannelOption<?>, Object> getOptions() {
83          return getOptions(
84                  null,
85                  CONNECT_TIMEOUT_MILLIS, MAX_MESSAGES_PER_READ, WRITE_SPIN_COUNT,
86                  ALLOCATOR, AUTO_READ, RCVBUF_ALLOCATOR, WRITE_BUFFER_HIGH_WATER_MARK,
87                  WRITE_BUFFER_LOW_WATER_MARK, MESSAGE_SIZE_ESTIMATOR);
88      }
89  
90      protected Map<ChannelOption<?>, Object> getOptions(
91              Map<ChannelOption<?>, Object> result, ChannelOption<?>... options) {
92          if (result == null) {
93              result = new IdentityHashMap<ChannelOption<?>, Object>();
94          }
95          for (ChannelOption<?> o: options) {
96              result.put(o, getOption(o));
97          }
98          return result;
99      }
100 
101     @SuppressWarnings("unchecked")
102     @Override
103     public boolean setOptions(Map<ChannelOption<?>, ?> options) {
104         if (options == null) {
105             throw new NullPointerException("options");
106         }
107 
108         boolean setAllOptions = true;
109         for (Entry<ChannelOption<?>, ?> e: options.entrySet()) {
110             if (!setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
111                 setAllOptions = false;
112             }
113         }
114 
115         return setAllOptions;
116     }
117 
118     @Override
119     @SuppressWarnings("unchecked")
120     public <T> T getOption(ChannelOption<T> option) {
121         if (option == null) {
122             throw new NullPointerException("option");
123         }
124 
125         if (option == CONNECT_TIMEOUT_MILLIS) {
126             return (T) Integer.valueOf(getConnectTimeoutMillis());
127         }
128         if (option == MAX_MESSAGES_PER_READ) {
129             return (T) Integer.valueOf(getMaxMessagesPerRead());
130         }
131         if (option == WRITE_SPIN_COUNT) {
132             return (T) Integer.valueOf(getWriteSpinCount());
133         }
134         if (option == ALLOCATOR) {
135             return (T) getAllocator();
136         }
137         if (option == RCVBUF_ALLOCATOR) {
138             return (T) getRecvByteBufAllocator();
139         }
140         if (option == AUTO_READ) {
141             return (T) Boolean.valueOf(isAutoRead());
142         }
143         if (option == WRITE_BUFFER_HIGH_WATER_MARK) {
144             return (T) Integer.valueOf(getWriteBufferHighWaterMark());
145         }
146         if (option == WRITE_BUFFER_LOW_WATER_MARK) {
147             return (T) Integer.valueOf(getWriteBufferLowWaterMark());
148         }
149         if (option == MESSAGE_SIZE_ESTIMATOR) {
150             return (T) getMessageSizeEstimator();
151         }
152         return null;
153     }
154 
155     @Override
156     public <T> boolean setOption(ChannelOption<T> option, T value) {
157         validate(option, value);
158 
159         if (option == CONNECT_TIMEOUT_MILLIS) {
160             setConnectTimeoutMillis((Integer) value);
161         } else if (option == MAX_MESSAGES_PER_READ) {
162             setMaxMessagesPerRead((Integer) value);
163         } else if (option == WRITE_SPIN_COUNT) {
164             setWriteSpinCount((Integer) value);
165         } else if (option == ALLOCATOR) {
166             setAllocator((ByteBufAllocator) value);
167         } else if (option == RCVBUF_ALLOCATOR) {
168             setRecvByteBufAllocator((RecvByteBufAllocator) value);
169         } else if (option == AUTO_READ) {
170             setAutoRead((Boolean) value);
171         } else if (option == WRITE_BUFFER_HIGH_WATER_MARK) {
172             setWriteBufferHighWaterMark((Integer) value);
173         } else if (option == WRITE_BUFFER_LOW_WATER_MARK) {
174             setWriteBufferLowWaterMark((Integer) value);
175         } else if (option == MESSAGE_SIZE_ESTIMATOR) {
176             setMessageSizeEstimator((MessageSizeEstimator) value);
177         } else {
178             return false;
179         }
180 
181         return true;
182     }
183 
184     protected <T> void validate(ChannelOption<T> option, T value) {
185         if (option == null) {
186             throw new NullPointerException("option");
187         }
188         option.validate(value);
189     }
190 
191     @Override
192     public int getConnectTimeoutMillis() {
193         return connectTimeoutMillis;
194     }
195 
196     @Override
197     public ChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
198         if (connectTimeoutMillis < 0) {
199             throw new IllegalArgumentException(String.format(
200                     "connectTimeoutMillis: %d (expected: >= 0)", connectTimeoutMillis));
201         }
202         this.connectTimeoutMillis = connectTimeoutMillis;
203         return this;
204     }
205 
206     @Override
207     public int getMaxMessagesPerRead() {
208         return maxMessagesPerRead;
209     }
210 
211     @Override
212     public ChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
213         if (maxMessagesPerRead <= 0) {
214             throw new IllegalArgumentException("maxMessagesPerRead: " + maxMessagesPerRead + " (expected: > 0)");
215         }
216         this.maxMessagesPerRead = maxMessagesPerRead;
217         return this;
218     }
219 
220     @Override
221     public int getWriteSpinCount() {
222         return writeSpinCount;
223     }
224 
225     @Override
226     public ChannelConfig setWriteSpinCount(int writeSpinCount) {
227         if (writeSpinCount <= 0) {
228             throw new IllegalArgumentException(
229                     "writeSpinCount must be a positive integer.");
230         }
231         this.writeSpinCount = writeSpinCount;
232         return this;
233     }
234 
235     @Override
236     public ByteBufAllocator getAllocator() {
237         return allocator;
238     }
239 
240     @Override
241     public ChannelConfig setAllocator(ByteBufAllocator allocator) {
242         if (allocator == null) {
243             throw new NullPointerException("allocator");
244         }
245         this.allocator = allocator;
246         return this;
247     }
248 
249     @Override
250     public RecvByteBufAllocator getRecvByteBufAllocator() {
251         return rcvBufAllocator;
252     }
253 
254     @Override
255     public ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
256         if (allocator == null) {
257             throw new NullPointerException("allocator");
258         }
259         rcvBufAllocator = allocator;
260         return this;
261     }
262 
263     @Override
264     public boolean isAutoRead() {
265         return autoRead == 1;
266     }
267 
268     @Override
269     public ChannelConfig setAutoRead(boolean autoRead) {
270         boolean oldAutoRead = AUTOREAD_UPDATER.getAndSet(this, autoRead ? 1 : 0) == 1;
271         if (autoRead && !oldAutoRead) {
272             channel.read();
273         } else if (!autoRead && oldAutoRead) {
274             autoReadCleared();
275         }
276         return this;
277     }
278 
279     /**
280      * Is called once {@link #setAutoRead(boolean)} is called with {@code false} and {@link #isAutoRead()} was
281      * {@code true} before.
282      */
283     protected void autoReadCleared() { }
284 
285     @Override
286     public int getWriteBufferHighWaterMark() {
287         return writeBufferHighWaterMark;
288     }
289 
290     @Override
291     public ChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
292         if (writeBufferHighWaterMark < getWriteBufferLowWaterMark()) {
293             throw new IllegalArgumentException(
294                     "writeBufferHighWaterMark cannot be less than " +
295                             "writeBufferLowWaterMark (" + getWriteBufferLowWaterMark() + "): " +
296                             writeBufferHighWaterMark);
297         }
298         if (writeBufferHighWaterMark < 0) {
299             throw new IllegalArgumentException(
300                     "writeBufferHighWaterMark must be >= 0");
301         }
302         this.writeBufferHighWaterMark = writeBufferHighWaterMark;
303         return this;
304     }
305 
306     @Override
307     public int getWriteBufferLowWaterMark() {
308         return writeBufferLowWaterMark;
309     }
310 
311     @Override
312     public ChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
313         if (writeBufferLowWaterMark > getWriteBufferHighWaterMark()) {
314             throw new IllegalArgumentException(
315                     "writeBufferLowWaterMark cannot be greater than " +
316                             "writeBufferHighWaterMark (" + getWriteBufferHighWaterMark() + "): " +
317                             writeBufferLowWaterMark);
318         }
319         if (writeBufferLowWaterMark < 0) {
320             throw new IllegalArgumentException(
321                     "writeBufferLowWaterMark must be >= 0");
322         }
323         this.writeBufferLowWaterMark = writeBufferLowWaterMark;
324         return this;
325     }
326 
327     @Override
328     public MessageSizeEstimator getMessageSizeEstimator() {
329         return msgSizeEstimator;
330     }
331 
332     @Override
333     public ChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
334         if (estimator == null) {
335             throw new NullPointerException("estimator");
336         }
337         msgSizeEstimator = estimator;
338         return this;
339     }
340 }