1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.nio.channels.spi; 19 20import java.io.IOException; 21import java.nio.channels.CancelledKeyException; 22import java.nio.channels.ClosedChannelException; 23import java.nio.channels.IllegalBlockingModeException; 24import java.nio.channels.IllegalSelectorException; 25import java.nio.channels.SelectableChannel; 26import java.nio.channels.SelectionKey; 27import java.nio.channels.Selector; 28import java.util.ArrayList; 29import java.util.List; 30 31/** 32 * {@code AbstractSelectableChannel} is the base implementation class for 33 * selectable channels. It declares methods for registering, unregistering and 34 * closing selectable channels. It is thread-safe. 35 */ 36public abstract class AbstractSelectableChannel extends SelectableChannel { 37 38 private final SelectorProvider provider; 39 40 /* 41 * The collection of key. 42 */ 43 private List<SelectionKey> keyList = new ArrayList<SelectionKey>(); 44 45 private final Object blockingLock = new Object(); 46 47 boolean isBlocking = true; 48 49 /** 50 * Constructs a new {@code AbstractSelectableChannel}. 51 * 52 * @param selectorProvider 53 * the selector provider that creates this channel. 54 */ 55 protected AbstractSelectableChannel(SelectorProvider selectorProvider) { 56 provider = selectorProvider; 57 } 58 59 /** 60 * Returns the selector provider that has created this channel. 61 * 62 * @see java.nio.channels.SelectableChannel#provider() 63 * @return this channel's selector provider. 64 */ 65 @Override 66 public final SelectorProvider provider() { 67 return provider; 68 } 69 70 /** 71 * Indicates whether this channel is registered with one or more selectors. 72 * 73 * @return {@code true} if this channel is registered with a selector, 74 * {@code false} otherwise. 75 */ 76 @Override 77 synchronized public final boolean isRegistered() { 78 return !keyList.isEmpty(); 79 } 80 81 /** 82 * Gets this channel's selection key for the specified selector. 83 * 84 * @param selector 85 * the selector with which this channel has been registered. 86 * @return the selection key for the channel or {@code null} if this channel 87 * has not been registered with {@code selector}. 88 */ 89 @Override 90 synchronized public final SelectionKey keyFor(Selector selector) { 91 for (SelectionKey key : keyList) { 92 if (key != null && key.selector() == selector) { 93 return key; 94 } 95 } 96 return null; 97 } 98 99 /** 100 * Registers this channel with the specified selector for the specified 101 * interest set. If the channel is already registered with the selector, the 102 * {@link SelectionKey interest set} is updated to {@code interestSet} and 103 * the corresponding selection key is returned. If the channel is not yet 104 * registered, this method calls the {@code register} method of 105 * {@code selector} and adds the selection key to this channel's key set. 106 * 107 * @param selector 108 * the selector with which to register this channel. 109 * @param interestSet 110 * this channel's {@link SelectionKey interest set}. 111 * @param attachment 112 * the object to attach, can be {@code null}. 113 * @return the selection key for this registration. 114 * @throws CancelledKeyException 115 * if this channel is registered but its key has been canceled. 116 * @throws ClosedChannelException 117 * if this channel is closed. 118 * @throws IllegalArgumentException 119 * if {@code interestSet} is not supported by this channel. 120 * @throws IllegalBlockingModeException 121 * if this channel is in blocking mode. 122 * @throws IllegalSelectorException 123 * if this channel does not have the same provider as the given 124 * selector. 125 */ 126 @Override 127 public final SelectionKey register(Selector selector, int interestSet, 128 Object attachment) throws ClosedChannelException { 129 if (!isOpen()) { 130 throw new ClosedChannelException(); 131 } 132 if (!((interestSet & ~validOps()) == 0)) { 133 throw new IllegalArgumentException("no valid ops in interest set: " + interestSet); 134 } 135 136 synchronized (blockingLock) { 137 if (isBlocking) { 138 throw new IllegalBlockingModeException(); 139 } 140 if (!selector.isOpen()) { 141 if (interestSet == 0) { 142 // throw ISE exactly to keep consistency 143 throw new IllegalSelectorException(); 144 } 145 // throw NPE exactly to keep consistency 146 throw new NullPointerException("selector not open"); 147 } 148 SelectionKey key = keyFor(selector); 149 if (key == null) { 150 key = ((AbstractSelector) selector).register(this, interestSet, attachment); 151 keyList.add(key); 152 } else { 153 if (!key.isValid()) { 154 throw new CancelledKeyException(); 155 } 156 key.interestOps(interestSet); 157 key.attach(attachment); 158 } 159 return key; 160 } 161 } 162 163 /** 164 * Implements the channel closing behavior. Calls 165 * {@code implCloseSelectableChannel()} first, then loops through the list 166 * of selection keys and cancels them, which unregisters this channel from 167 * all selectors it is registered with. 168 * 169 * @throws IOException 170 * if a problem occurs while closing the channel. 171 */ 172 @Override 173 synchronized protected final void implCloseChannel() throws IOException { 174 implCloseSelectableChannel(); 175 for (SelectionKey key : keyList) { 176 if (key != null) { 177 key.cancel(); 178 } 179 } 180 } 181 182 /** 183 * Implements the closing function of the SelectableChannel. This method is 184 * called from {@code implCloseChannel()}. 185 * 186 * @throws IOException 187 * if an I/O exception occurs. 188 */ 189 protected abstract void implCloseSelectableChannel() throws IOException; 190 191 /** 192 * Indicates whether this channel is in blocking mode. 193 * 194 * @return {@code true} if this channel is blocking, {@code false} 195 * otherwise. 196 */ 197 @Override 198 public final boolean isBlocking() { 199 synchronized (blockingLock) { 200 return isBlocking; 201 } 202 } 203 204 /** 205 * Gets the object used for the synchronization of {@code register} and 206 * {@code configureBlocking}. 207 * 208 * @return the synchronization object. 209 */ 210 @Override 211 public final Object blockingLock() { 212 return blockingLock; 213 } 214 215 /** 216 * Sets the blocking mode of this channel. A call to this method blocks if 217 * other calls to this method or to {@code register} are executing. The 218 * actual setting of the mode is done by calling 219 * {@code implConfigureBlocking(boolean)}. 220 * 221 * @see java.nio.channels.SelectableChannel#configureBlocking(boolean) 222 * @param blockingMode 223 * {@code true} for setting this channel's mode to blocking, 224 * {@code false} to set it to non-blocking. 225 * @return this channel. 226 * @throws ClosedChannelException 227 * if this channel is closed. 228 * @throws IllegalBlockingModeException 229 * if {@code block} is {@code true} and this channel has been 230 * registered with at least one selector. 231 * @throws IOException 232 * if an I/O error occurs. 233 */ 234 @Override 235 public final SelectableChannel configureBlocking(boolean blockingMode) throws IOException { 236 if (!isOpen()) { 237 throw new ClosedChannelException(); 238 } 239 synchronized (blockingLock) { 240 if (isBlocking == blockingMode) { 241 return this; 242 } 243 if (blockingMode && containsValidKeys()) { 244 throw new IllegalBlockingModeException(); 245 } 246 implConfigureBlocking(blockingMode); 247 isBlocking = blockingMode; 248 } 249 return this; 250 } 251 252 /** 253 * Implements the configuration of blocking/non-blocking mode. 254 * 255 * @param blocking true for blocking, false for non-blocking. 256 * @throws IOException 257 * if an I/O error occurs. 258 */ 259 protected abstract void implConfigureBlocking(boolean blocking) throws IOException; 260 261 /* 262 * package private for deregister method in AbstractSelector. 263 */ 264 synchronized void deregister(SelectionKey k) { 265 if (keyList != null) { 266 keyList.remove(k); 267 } 268 } 269 270 /** 271 * Returns true if the keyList contains at least 1 valid key and false 272 * otherwise. 273 */ 274 private synchronized boolean containsValidKeys() { 275 for (SelectionKey key : keyList) { 276 if (key != null && key.isValid()) { 277 return true; 278 } 279 } 280 return false; 281 } 282} 283