Home | History | Annotate | Download | only in spi
      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 
     18 package java.nio.channels.spi;
     19 
     20 import java.io.IOException;
     21 import java.nio.channels.CancelledKeyException;
     22 import java.nio.channels.ClosedChannelException;
     23 import java.nio.channels.IllegalBlockingModeException;
     24 import java.nio.channels.IllegalSelectorException;
     25 import java.nio.channels.SelectableChannel;
     26 import java.nio.channels.SelectionKey;
     27 import java.nio.channels.Selector;
     28 import java.util.ArrayList;
     29 import 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  */
     36 public 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