001    /* AbstractSelectableChannel.java
002       Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.nio.channels.spi;
040    
041    import java.io.IOException;
042    import java.nio.channels.ClosedChannelException;
043    import java.nio.channels.SelectableChannel;
044    import java.nio.channels.SelectionKey;
045    import java.nio.channels.Selector;
046    import java.nio.channels.IllegalBlockingModeException;
047    import java.util.Iterator;
048    import java.util.LinkedList;
049    import java.util.ListIterator;
050    
051    public abstract class AbstractSelectableChannel extends SelectableChannel
052    {
053      private boolean blocking = true;
054      private Object LOCK = new Object();
055      private SelectorProvider provider;
056      private LinkedList keys = new LinkedList();
057    
058      /**
059       * Initializes the channel
060       *
061       * @param provider the provider that created this channel
062       */
063      protected AbstractSelectableChannel(SelectorProvider provider)
064      {
065        this.provider = provider;
066      }
067    
068      /**
069       * Retrieves the object upon which the configureBlocking and register
070       * methods synchronize.
071       *
072       * @return the blocking lock
073       */
074      public final Object blockingLock()
075      {
076        return LOCK;
077      }
078    
079      /**
080       * Adjusts this channel's blocking mode.
081       *
082       * @param blocking true if blocking should be enabled, false otherwise
083       *
084       * @return this channel
085       *
086       * @exception IOException If an error occurs
087       */
088      public final SelectableChannel configureBlocking(boolean blocking)
089        throws IOException
090      {
091        synchronized (blockingLock())
092          {
093            if (this.blocking != blocking)
094              {
095                implConfigureBlocking(blocking);
096                this.blocking = blocking;
097              }
098          }
099    
100        return this;
101      }
102    
103      /**
104       * Closes this channel.
105       *
106       * @exception IOException If an error occurs
107       */
108      protected final void implCloseChannel() throws IOException
109      {
110        try
111          {
112            implCloseSelectableChannel();
113          }
114        finally
115          {
116            for (Iterator it = keys.iterator(); it.hasNext(); )
117              ((SelectionKey) it.next()).cancel();
118          }
119      }
120    
121      /**
122       * Closes this selectable channel.
123       *
124       * @exception IOException If an error occurs
125       */
126      protected abstract void implCloseSelectableChannel()
127        throws IOException;
128    
129      /**
130       * Adjusts this channel's blocking mode.
131       *
132       * @param blocking true if blocking should be enabled, false otherwise
133       *
134       * @exception IOException If an error occurs
135       */
136      protected abstract void implConfigureBlocking(boolean blocking)
137        throws IOException;
138    
139      /**
140       * Tells whether or not every I/O operation on this channel will block
141       * until it completes.
142       *
143       * @return true of this channel is blocking, false otherwise
144       */
145      public final boolean isBlocking()
146      {
147        return blocking;
148      }
149    
150      /**
151       * Tells whether or not this channel is currently registered with
152       * any selectors.
153       *
154       * @return true if this channel is registered, false otherwise
155       */
156      public final boolean isRegistered()
157      {
158        return ! keys.isEmpty();
159      }
160    
161      /**
162       * Retrieves the key representing the channel's registration with the
163       * given selector.
164       *
165       * @param selector the selector to get a selection key for
166       *
167       * @return the selection key this channel is registered with
168       */
169      public final SelectionKey keyFor(Selector selector)
170      {
171        if (! isOpen())
172          return null;
173    
174        try
175          {
176            synchronized (blockingLock())
177              {
178                return locate(selector);
179              }
180          }
181        catch (Exception e)
182          {
183            return null;
184          }
185      }
186    
187      /**
188       * Returns the provider that created this channel.
189       *
190       * @return the selector provider that created this channel
191       */
192      public final SelectorProvider provider()
193      {
194        return provider;
195      }
196    
197      private SelectionKey locate(Selector selector)
198      {
199        ListIterator it = keys.listIterator();
200    
201        while (it.hasNext())
202          {
203            SelectionKey key = (SelectionKey) it.next();
204    
205            if (key.selector() == selector)
206              return key;
207          }
208    
209        return null;
210      }
211    
212      /**
213       * Registers this channel with the given selector, returning a selection key.
214       *
215       * @param selin the seletor to use
216       * @param ops the interested operations
217       * @param att an attachment for the returned selection key
218       *
219       * @return the registered selection key
220       *
221       * @exception ClosedChannelException If the channel is already closed.
222       * @exception IllegalBlockingModeException If the channel is configured in
223       * blocking mode.
224       */
225      public final SelectionKey register(Selector selin, int ops, Object att)
226        throws ClosedChannelException
227      {
228        if (! isOpen())
229          throw new ClosedChannelException();
230    
231        if ((ops & ~validOps()) != 0)
232          throw new IllegalArgumentException();
233    
234        SelectionKey key = null;
235        AbstractSelector selector = (AbstractSelector) selin;
236    
237        synchronized (blockingLock())
238          {
239            if (blocking)
240              throw new IllegalBlockingModeException();
241    
242            key = locate(selector);
243    
244            if (key != null && key.isValid())
245              {
246                key.interestOps(ops);
247                key.attach(att);
248              }
249            else
250              {
251                key = selector.register(this, ops, att);
252    
253                if (key != null)
254                  addSelectionKey(key);
255              }
256          }
257    
258        return key;
259      }
260    
261      void addSelectionKey(SelectionKey key)
262      {
263        keys.add(key);
264      }
265    
266      // This method gets called by AbstractSelector.deregister().
267      void removeSelectionKey(SelectionKey key)
268      {
269        keys.remove(key);
270      }
271    }