001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.nio.channels.spi;
019:
020: import java.io.IOException;
021: import java.nio.channels.CancelledKeyException;
022: import java.nio.channels.ClosedChannelException;
023: import java.nio.channels.IllegalBlockingModeException;
024: import java.nio.channels.IllegalSelectorException;
025: import java.nio.channels.SelectableChannel;
026: import java.nio.channels.SelectionKey;
027: import java.nio.channels.Selector;
028: import java.util.ArrayList;
029: import java.util.List;
030:
031: /**
032: * Abstract class for selectable channels.
033: * <p>
034: * In this class, there are methods about registering/deregistering a channel,
035: * about channel closing. It realize the multi-thread safe.
036: * </p>
037: */
038: public abstract class AbstractSelectableChannel extends
039: SelectableChannel {
040:
041: private final SelectorProvider provider;
042:
043: /*
044: * The collection of key.
045: */
046: private List<SelectionKey> keyList = new ArrayList<SelectionKey>();
047:
048: // Marker class so lock type shows up in profilers
049: static private class BlockingLock {
050: }
051:
052: private final Object blockingLock = new BlockingLock();
053:
054: boolean isBlocking = true;
055:
056: /**
057: * Constructor for this class.
058: *
059: * @param selectorProvider
060: * A instance of SelectorProvider
061: */
062: protected AbstractSelectableChannel(
063: SelectorProvider selectorProvider) {
064: super ();
065: provider = selectorProvider;
066: }
067:
068: /**
069: * Answer the SelectorProvider of this channel.
070: *
071: * @see java.nio.channels.SelectableChannel#provider()
072: * @return The provider of this channel.
073: */
074: @Override
075: public final SelectorProvider provider() {
076: return provider;
077: }
078:
079: /**
080: * @see java.nio.channels.SelectableChannel#isRegistered()
081: */
082: @Override
083: synchronized public final boolean isRegistered() {
084: return !keyList.isEmpty();
085: }
086:
087: /**
088: * @see java.nio.channels.SelectableChannel#keyFor(java.nio.channels.Selector)
089: */
090: @Override
091: synchronized public final SelectionKey keyFor(Selector selector) {
092: for (int i = 0; i < keyList.size(); i++) {
093: SelectionKey key = keyList.get(i);
094: if (null != key && key.selector() == selector) {
095: return key;
096: }
097: }
098: return null;
099: }
100:
101: /**
102: * Realize the register function.
103: * <p>
104: * It registers current channel to the selector, then answer the selection
105: * key. The channel must be open and the interest op set must be valid. If
106: * the current channel is already registered to the selector, the method
107: * only set the new interest op set; otherwise it will call the
108: * <code>register</code> in <code>selector</code>, and add the relative
109: * key to the key set of the current channel.
110: * </p>
111: *
112: * @see java.nio.channels.SelectableChannel#register(java.nio.channels.Selector,
113: * int, java.lang.Object)
114: */
115: @Override
116: public final SelectionKey register(Selector selector,
117: int interestSet, Object attachment)
118: throws ClosedChannelException {
119: if (!isOpen()) {
120: throw new ClosedChannelException();
121: }
122: if (!((interestSet & ~validOps()) == 0)) {
123: throw new IllegalArgumentException();
124: }
125:
126: synchronized (blockingLock) {
127: if (isBlocking) {
128: throw new IllegalBlockingModeException();
129: }
130: if (!selector.isOpen()) {
131: if (0 == interestSet) {
132: // throw ISE exactly to keep consistency
133: throw new IllegalSelectorException();
134: }
135: // throw NPE exactly to keep consistency
136: throw new NullPointerException();
137: }
138: if (0 == interestSet) {
139: // throw ISE exactly to keep consistency
140: throw new IllegalSelectorException();
141: }
142: SelectionKey key = keyFor(selector);
143: if (null == key) {
144: key = ((AbstractSelector) selector).register(this ,
145: interestSet, attachment);
146: keyList.add(key);
147: } else {
148: if (!key.isValid()) {
149: throw new CancelledKeyException();
150: }
151: key.interestOps(interestSet);
152: key.attach(attachment);
153: }
154: return key;
155: }
156: }
157:
158: /**
159: * Implement the closing function.
160: *
161: * @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel()
162: */
163: @Override
164: synchronized protected final void implCloseChannel()
165: throws IOException {
166: implCloseSelectableChannel();
167: for (int i = 0; i < keyList.size(); i++) {
168: SelectionKey key = keyList.get(i);
169: if (null != key) {
170: key.cancel();
171: }
172: }
173: }
174:
175: /**
176: * Implement the closing function of the SelectableChannel.
177: *
178: * @throws IOException
179: * If some I/O exception occurred.
180: */
181: protected abstract void implCloseSelectableChannel()
182: throws IOException;
183:
184: /**
185: * @see java.nio.channels.SelectableChannel#isBlocking()
186: */
187: @Override
188: public final boolean isBlocking() {
189: synchronized (blockingLock) {
190: return isBlocking;
191: }
192: }
193:
194: /**
195: * @see java.nio.channels.SelectableChannel#blockingLock()
196: */
197: @Override
198: public final Object blockingLock() {
199: return blockingLock;
200: }
201:
202: /**
203: * Set the blocking mode of this channel.
204: *
205: * @see java.nio.channels.SelectableChannel#configureBlocking(boolean)
206: * @param blockingMode
207: * <code>true</code> for blocking mode; <code>false</code>
208: * for non-blocking mode.
209: */
210: @Override
211: public final SelectableChannel configureBlocking(
212: boolean blockingMode) throws IOException {
213: if (isOpen()) {
214: synchronized (blockingLock) {
215: if (isBlocking == blockingMode) {
216: return this ;
217: }
218: if (blockingMode && containsValidKeys()) {
219: throw new IllegalBlockingModeException();
220: }
221: implConfigureBlocking(blockingMode);
222: isBlocking = blockingMode;
223: }
224: return this ;
225: }
226: throw new ClosedChannelException();
227: }
228:
229: /**
230: * Implement the setting of blocking mode.
231: *
232: * @param blockingMode
233: * <code>true</code> for blocking mode; <code>false</code>
234: * for non-blocking mode.
235: * @throws IOException
236: * If some I/O exception occurred.
237: */
238: protected abstract void implConfigureBlocking(boolean blockingMode)
239: throws IOException;
240:
241: /*
242: * package private for deregister method in AbstractSelector.
243: */
244: synchronized void deregister(SelectionKey k) {
245: if (null != keyList) {
246: keyList.remove(k);
247: }
248: }
249:
250: /**
251: * Returns true if the keyList contains at least 1 valid key and false
252: * otherwise.
253: */
254: private synchronized boolean containsValidKeys() {
255: for (int i = 0; i < keyList.size(); i++) {
256: SelectionKey key = keyList.get(i);
257: if (key != null && key.isValid()) {
258: return true;
259: }
260: }
261: return false;
262: }
263: }
|