View Javadoc
1   /*
2    * Copyright 2022 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    *   https://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.netty5.channel.nio;
17  
18  import io.netty5.channel.IoHandle;
19  import io.netty5.util.internal.logging.InternalLogger;
20  import io.netty5.util.internal.logging.InternalLoggerFactory;
21  
22  import java.io.IOException;
23  import java.nio.channels.ClosedChannelException;
24  import java.nio.channels.SelectableChannel;
25  import java.nio.channels.SelectionKey;
26  import java.nio.channels.Selector;
27  import java.util.function.BiConsumer;
28  
29  import static java.util.Objects.requireNonNull;
30  
31  /**
32   * Allows to create an {@link IoHandle} for a {@link SelectableChannel}, not necessarily created by Netty. This
33   * {@link IoHandle} can be used together with {@link NioHandler} and so have events dispatched for
34   * the {@link SelectableChannel}.
35   */
36  public final class NioSelectableChannelHandle<S extends SelectableChannel> implements IoHandle {
37      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSelectableChannelHandle.class);
38  
39      private final S channel;
40      private final int interestOps;
41      private volatile SelectionKey selectionKey;
42  
43      private final BiConsumer<S, SelectionKey> keyProcessor;
44  
45      public NioSelectableChannelHandle(S channel, int interestOps, BiConsumer<S, SelectionKey> keyProcessor) {
46          if ((interestOps & ~channel.validOps()) != 0) {
47              throw new IllegalArgumentException(
48                      "invalid interestOps: " + interestOps + "(validOps: " + channel.validOps() + ')');
49          }
50          this.channel = requireNonNull(channel, "channel");
51          this.interestOps = interestOps;
52          this.keyProcessor = requireNonNull(keyProcessor, "keyProcessor");
53      }
54  
55      @Override
56      public boolean isRegistered() {
57          return channel.isRegistered();
58      }
59  
60      private final NioProcessor nioProcessor = new NioProcessor() {
61          @Override
62          public void register(Selector selector) throws ClosedChannelException {
63              int interestOps;
64              SelectionKey key = selectionKey;
65              if (key != null) {
66                  interestOps = key.interestOps();
67                  key.cancel();
68              } else {
69                  interestOps = NioSelectableChannelHandle.this.interestOps;
70              }
71              selectionKey = channel.register(selector, interestOps, this);
72          }
73  
74          @Override
75          public void deregister() {
76              SelectionKey key = selectionKey;
77              if (key != null) {
78                  key.cancel();
79              }
80          }
81  
82          @Override
83          public void handle(SelectionKey key) {
84              keyProcessor.accept(channel, key);
85          }
86  
87          @Override
88          public void close() {
89              try {
90                  channel.close();
91              } catch (IOException e) {
92                  logger.warn("Unexpected exception while closing underlying channel", e);
93              }
94          }
95      };
96  
97      NioProcessor nioProcessor() {
98          return nioProcessor;
99      }
100 }