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.lang.reflect.Method;
022: import java.nio.channels.AsynchronousCloseException;
023: import java.nio.channels.Channel;
024: import java.nio.channels.ClosedByInterruptException;
025: import java.nio.channels.InterruptibleChannel;
026: import java.security.AccessController;
027: import java.security.PrivilegedActionException;
028: import java.security.PrivilegedExceptionAction;
029:
030: /**
031: * This class roots the implementation of interruptible channels.
032: * <p>
033: * The basic usage pattern for an interruptible channel is to invoke
034: * <code>begin()</code> before any IO operations, then
035: * <code>end(boolean)</code> after completing the operation. The argument to
036: * the end method shows whether there has been any change to the java
037: * environment that is visible to the API user.
038: * </p>
039: */
040: public abstract class AbstractInterruptibleChannel implements Channel,
041: InterruptibleChannel {
042:
043: static Method setInterruptAction = null;
044:
045: static {
046: try {
047: setInterruptAction = AccessController
048: .doPrivileged(new PrivilegedExceptionAction<Method>() {
049: public Method run() throws Exception {
050: return Thread.class.getDeclaredMethod(
051: "setInterruptAction", //$NON-NLS-1$
052: new Class[] { Runnable.class });
053:
054: }
055: });
056: setInterruptAction.setAccessible(true);
057: } catch (PrivilegedActionException e) {
058: // FIXME: be accommodate before VM actually provides
059: // setInterruptAction method
060: // throw new Error(e);
061: }
062: }
063:
064: private volatile boolean closed = false;
065:
066: volatile boolean interrupted = false;
067:
068: /**
069: * Default constructor.
070: */
071: protected AbstractInterruptibleChannel() {
072: super ();
073: }
074:
075: /**
076: * Answers whether the channel is open.
077: *
078: * @return true if the channel is open, and false if it is closed.
079: * @see java.nio.channels.Channel#isOpen()
080: */
081: public synchronized final boolean isOpen() {
082: return !closed;
083: }
084:
085: /**
086: * Closes the channel.
087: * <p>
088: * If the channel is already closed then this method has no effect,
089: * otherwise it closes the receiver via the implCloseChannel method.
090: * </p>
091: *
092: * @see java.nio.channels.Channel#close()
093: */
094: public final void close() throws IOException {
095: if (!closed) {
096: synchronized (this ) {
097: if (!closed) {
098: closed = true;
099: implCloseChannel();
100: }
101: }
102: }
103: }
104:
105: /**
106: * Start an IO operation that is potentially blocking.
107: * <p>
108: * Once the operation is completed the application should invoke a
109: * corresponding <code>end(boolean)</code>.
110: */
111: protected final void begin() {
112: // FIXME: be accommodate before VM actually provides
113: // setInterruptAction method
114: if (setInterruptAction != null) {
115: try {
116: setInterruptAction.invoke(Thread.currentThread(),
117: new Object[] { new Runnable() {
118: public void run() {
119: try {
120: interrupted = true;
121: AbstractInterruptibleChannel.this
122: .close();
123: } catch (IOException e) {
124: // ignore
125: }
126: }
127: } });
128: } catch (Exception e) {
129: throw new RuntimeException(e);
130: }
131: }
132: }
133:
134: /**
135: * End an IO operation that was previously started with <code>begin()</code>.
136: *
137: * @param success
138: * pass true if the operation succeeded and had a side effect on
139: * the Java system, or false if not.
140: * @throws AsynchronousCloseException
141: * the channel was closed while the IO operation was in
142: * progress.
143: * @throws java.nio.channels.ClosedByInterruptException
144: * the thread conducting the IO operation was interrupted.
145: */
146: protected final void end(boolean success)
147: throws AsynchronousCloseException {
148: // FIXME: be accommodate before VM actually provides
149: // setInterruptAction method
150: if (setInterruptAction != null) {
151: try {
152: setInterruptAction.invoke(Thread.currentThread(),
153: new Object[] { null });
154: } catch (Exception e) {
155: throw new RuntimeException(e);
156: }
157: if (interrupted) {
158: interrupted = false;
159: throw new ClosedByInterruptException();
160: }
161: }
162: if (!success && closed) {
163: throw new AsynchronousCloseException();
164: }
165: }
166:
167: /**
168: * Implements the close channel behavior.
169: * <p>
170: * Closes the channel with a guarantee that the channel is not currently
171: * closed via <code>close()</code> and that the method is thread-safe.
172: * </p>
173: * <p>
174: * any outstanding threads blocked on IO operations on this channel must be
175: * released with either a normal return code, or an
176: * <code>AsynchronousCloseException</code>.
177: *
178: * @throws IOException
179: * if a problem occurs closing the channel.
180: */
181: protected abstract void implCloseChannel() throws IOException;
182: }
|