changeset 822:2ac3e64920d5

Allow AsynchronousChannelGroup be used as Executor (experimental use to allow development of SSL/TLS filter)
author alanb
date Fri, 14 Nov 2008 12:00:55 +0000
parents a7d137d96cf5
children d6c73618eaf6
files src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java src/share/classes/sun/nio/ch/DatagramChannelImpl.java src/share/classes/sun/nio/ch/FileChannelImpl.java src/share/classes/sun/nio/ch/Invoker.java src/share/classes/sun/nio/ch/PendingFuture.java src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java src/windows/classes/sun/nio/ch/Iocp.java src/windows/classes/sun/nio/ch/PendingIoCache.java src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java test/java/nio/channels/AsynchronousChannelGroup/Attack.java test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh
diffstat 17 files changed, 315 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -34,6 +34,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.security.PrivilegedAction;
 import java.security.AccessController;
+import java.security.AccessControlContext;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -41,7 +42,7 @@
  */
 
 abstract class AsynchronousChannelGroupImpl
-    extends AsynchronousChannelGroup
+    extends AsynchronousChannelGroup implements Executor
 {
     // number of internal threads handling I/O events when using an unbounded
     // thread pool. Internal threads do not dispatch to completion handlers.
@@ -189,7 +190,7 @@
      * events. For other thread pools we simply submit the task to the thread
      * pool.
      */
-    final void execute(Runnable task) {
+    final void executeOnPooledThread(Runnable task) {
         if (isFixedThreadPool()) {
             executeOnHandlerTask(task);
         } else {
@@ -305,4 +306,31 @@
     {
         return pool.executor().awaitTermination(timeout, unit);
     }
+
+    /**
+     * Executes the given command on one of the channel group's pooled threads.
+     */
+    @Override
+    public final void execute(Runnable task) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            // when a security manager is installed then the user's task
+            // must be run with the current calling context
+            final AccessControlContext acc = AccessController.getContext();
+            final Runnable delegate = task;
+            task = new Runnable() {
+                @Override
+                public void run() {
+                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                        @Override
+                        public Void run() {
+                            delegate.run();
+                            return null;
+                        }
+                    }, acc);
+                }
+            };
+        }
+        executeOnPooledThread(task);
+    }
 }
--- a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -192,6 +192,7 @@
                                                      A attachment,
                                                      CompletionHandler<V,? super A> handler);
 
+    @SuppressWarnings("unchecked")
     private <V extends Number,A> Future<V> read(ByteBuffer[] dsts,
                                                 boolean isScatteringRead,
                                                 long timeout,
@@ -290,6 +291,7 @@
                                                       A attachment,
                                                       CompletionHandler<V,? super A> handler);
 
+    @SuppressWarnings("unchecked")
     private <V extends Number,A> Future<V> write(ByteBuffer[] srcs,
                                                  boolean isGatheringWrite,
                                                  long timeout,
--- a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -40,7 +40,7 @@
 
 class DatagramChannelImpl
     extends DatagramChannel
-    implements MulticastChannel, SelChImpl
+    implements SelChImpl
 {
 
     // Used to make native read and write calls
--- a/src/share/classes/sun/nio/ch/FileChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -31,7 +31,6 @@
 import java.nio.MappedByteBuffer;
 import java.nio.BufferPoolMXBean;
 import java.nio.channels.*;
-import java.nio.channels.spi.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Iterator;
--- a/src/share/classes/sun/nio/ch/Invoker.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/Invoker.java	Fri Nov 14 12:00:55 2008 +0000
@@ -207,7 +207,7 @@
         if (handler != null) {
             AsynchronousChannel channel = result.channel();
             try {
-                ((Groupable)channel).group().execute(new Runnable() {
+                ((Groupable)channel).group().executeOnPooledThread(new Runnable() {
                     public void run() {
                         GroupAndInvokeCount thisGroupAndInvokeCount =
                             myGroupAndInvokeCount.get();
@@ -262,7 +262,7 @@
             if (invokeDirect) {
                 task.run();
             } else {
-                targetGroup.execute(task);
+                targetGroup.executeOnPooledThread(task);
             }
         } catch (RejectedExecutionException ree) {
             throw new ShutdownChannelGroupException();
--- a/src/share/classes/sun/nio/ch/PendingFuture.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/PendingFuture.java	Fri Nov 14 12:00:55 2008 +0000
@@ -221,7 +221,7 @@
                 };
                 AsynchronousChannel ch = channel();
                 if (ch instanceof Groupable) {
-                    ((Groupable)ch).group().execute(cancelTask);
+                    ((Groupable)ch).group().executeOnPooledThread(cancelTask);
                 } else {
                     if (ch instanceof AsynchronousFileChannelImpl) {
                         ((AsynchronousFileChannelImpl)ch).executor().execute(cancelTask);
--- a/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -292,7 +292,7 @@
         };
         Future<?> timeoutTask = scheduleTimeout(result, timeout, unit);
         try {
-            group.execute(task);
+            group.executeOnPooledThread(task);
         } catch (RejectedExecutionException ree) {
             if (timeoutTask != null)
                 timeoutTask.cancel(false);
@@ -345,7 +345,7 @@
         };
         Future<?> timeoutTask = scheduleTimeout(result, timeout, unit);
         try {
-            group.execute(task);
+            group.executeOnPooledThread(task);
         } catch (RejectedExecutionException ree) {
             if (timeoutTask != null)
                 timeoutTask.cancel(false);
--- a/src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java	Fri Nov 14 12:00:55 2008 +0000
@@ -28,7 +28,6 @@
 import java.nio.channels.*;
 import java.nio.channels.spi.AsynchronousChannelProvider;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadFactory;
 import java.net.ProtocolFamily;
 import java.io.IOException;
 
--- a/src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java	Fri Nov 14 12:00:55 2008 +0000
@@ -28,7 +28,6 @@
 import java.nio.channels.*;
 import java.nio.channels.spi.AsynchronousChannelProvider;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadFactory;
 import java.net.ProtocolFamily;
 import java.io.IOException;
 
--- a/src/windows/classes/sun/nio/ch/Iocp.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/windows/classes/sun/nio/ch/Iocp.java	Fri Nov 14 12:00:55 2008 +0000
@@ -350,6 +350,7 @@
                         if (result.isDone()) {
                             continue;
                         }
+                        // not handled by initiator
                     }
 
                     // invoke I/O result handler
--- a/src/windows/classes/sun/nio/ch/PendingIoCache.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/windows/classes/sun/nio/ch/PendingIoCache.java	Fri Nov 14 12:00:55 2008 +0000
@@ -71,7 +71,7 @@
     }
 
     long add(PendingFuture<?,?> result) {
-        synchronized (pendingIoMap) {
+        synchronized (this) {
             if (closed)
                 throw new AssertionError("Should not get here");
             long ov;
@@ -87,7 +87,7 @@
 
     @SuppressWarnings("unchecked")
     <V,A> PendingFuture<V,A> remove(long overlapped) {
-        synchronized (pendingIoMap) {
+        synchronized (this) {
             PendingFuture<V,A> res = pendingIoMap.remove(overlapped);
             if (res != null) {
                 if (overlappedCacheCount < overlappedCache.length) {
@@ -98,7 +98,7 @@
                 }
                 // notify closing thread.
                 if (closePending) {
-                    pendingIoMap.notifyAll();
+                    this.notifyAll();
                 }
             }
             return res;
@@ -106,7 +106,7 @@
     }
 
     void close() {
-        synchronized (pendingIoMap) {
+        synchronized (this) {
             if (closed)
                 return;
 
@@ -126,12 +126,12 @@
     }
 
     private void clearPendingIoMap() {
-        assert Thread.holdsLock(pendingIoMap);
+        assert Thread.holdsLock(this);
 
         // wait up to 50ms for the I/O operations to complete
         closePending = true;
         try {
-            pendingIoMap.wait(50);
+            this.wait(50);
         } catch (InterruptedException x) { }
         closePending = false;
         if (pendingIoMap.isEmpty())
@@ -154,7 +154,7 @@
                     rh.failed(-1, new AsynchronousCloseException());
                 }
             };
-            iocp.execute(task);
+            iocp.executeOnPooledThread(task);
         }
         pendingIoMap.clear();
     }
--- a/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -26,9 +26,7 @@
 package sun.nio.ch;
 
 import java.nio.channels.*;
-import java.util.*;
 import java.util.concurrent.*;
-import java.util.concurrent.locks.*;
 import java.nio.ByteBuffer;
 import java.nio.BufferOverflowException;
 import java.io.IOException;
--- a/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java	Fri Nov 14 11:00:20 2008 +0000
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java	Fri Nov 14 12:00:55 2008 +0000
@@ -29,9 +29,7 @@
 import java.nio.ByteBuffer;
 import java.nio.BufferOverflowException;
 import java.net.*;
-import java.util.*;
 import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
 import java.io.IOException;
 import sun.misc.Unsafe;
 
@@ -387,15 +385,20 @@
             shadow = new ByteBuffer[numBufs];
             long address = readBufferArray;
             for (int i=0; i<numBufs; i++) {
-                int rem = bufs[i].remaining();
+                ByteBuffer dst = bufs[i];
+                int pos = dst.position();
+                int lim = dst.limit();
+                assert (pos <= lim);
+                int rem = (pos <= lim ? lim - pos : 0);
                 long a;
-                if (!(bufs[i] instanceof DirectBuffer)) {
+                if (!(dst instanceof DirectBuffer)) {
                     // substitute with direct buffer
-                    shadow[i] = Util.getTemporaryDirectBuffer(rem);
-                    a = ((DirectBuffer)shadow[i]).address();
+                    ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+                    shadow[i] = bb;
+                    a = ((DirectBuffer)bb).address();
                 } else {
-                    shadow[i] = bufs[i];
-                    a = ((DirectBuffer)bufs[i]).address() + bufs[i].position();
+                    shadow[i] = dst;
+                    a = ((DirectBuffer)dst).address() + pos;
                 }
                 unsafe.putAddress(address + OFFSETOF_BUF, a);
                 unsafe.putInt(address + OFFSETOF_LEN, rem);
@@ -649,20 +652,23 @@
             shadow = new ByteBuffer[numBufs];
             long address = writeBufferArray;
             for (int i=0; i<numBufs; i++) {
-                int rem = bufs[i].remaining();
-                int pos = bufs[i].position();
+                ByteBuffer src = bufs[i];
+                int pos = src.position();
+                int lim = src.limit();
+                assert (pos <= lim);
+                int rem = (pos <= lim ? lim - pos : 0);
                 long a;
-                if (!(bufs[i] instanceof DirectBuffer)) {
+                if (!(src instanceof DirectBuffer)) {
                     // substitute with direct buffer
                     ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
-                    bb.put(bufs[i]);
+                    bb.put(src);
                     bb.flip();
-                    bufs[i].position(pos);  // leave heap buffer untouched for now
+                    src.position(pos);  // leave heap buffer untouched for now
                     shadow[i] = bb;
                     a = ((DirectBuffer)bb).address();
                 } else {
-                    shadow[i] = bufs[i];
-                    a = ((DirectBuffer)bufs[i]).address() + pos;
+                    shadow[i] = src;
+                    a = ((DirectBuffer)src).address() + pos;
                 }
                 unsafe.putAddress(address + OFFSETOF_BUF, a);
                 unsafe.putInt(address + OFFSETOF_LEN, rem);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java	Fri Nov 14 12:00:55 2008 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.nio.channels.AsynchronousChannelGroup;
+import java.util.concurrent.*;
+
+/**
+ * Test that arbitrary tasks can be submitted to a channel group's thread pool.
+ */
+
+public class AsExecutor {
+
+    public static void main(String[] args) throws Exception {
+        // create channel groups
+        ThreadFactory factory = new PrivilegedThreadFactory();
+        int nThreads = 5;
+        ExecutorService pool = Executors.newFixedThreadPool(5, factory);
+        AsynchronousChannelGroup group1 = AsynchronousChannelGroup
+            .withFixedThreadPool(pool, nThreads);
+        AsynchronousChannelGroup group2 = AsynchronousChannelGroup
+            .withCachedThreadPool(Executors.newCachedThreadPool(factory), 0);
+
+        try {
+            // execute simple tasks
+            testSimpleTask(group1);
+            testSimpleTask(group2);
+
+            // install security manager and test again
+            System.setSecurityManager( new SecurityManager() );
+            testSimpleTask(group1);
+            testSimpleTask(group2);
+
+            // attempt to execute tasks that run with only frames from boot
+            // class loader on the stack.
+            testAttackingTask(group1);
+            testAttackingTask(group2);
+        } finally {
+            group1.shutdown();
+            group2.shutdown();
+        }
+    }
+
+    static void testSimpleTask(AsynchronousChannelGroup group) throws Exception {
+        Executor executor = (Executor)group;
+        final CountDownLatch latch = new CountDownLatch(1);
+        executor.execute(new Runnable() {
+            public void run() {
+                latch.countDown();
+            }
+        });
+        latch.await();
+    }
+
+    static void testAttackingTask(AsynchronousChannelGroup group) throws Exception {
+        Executor executor = (Executor)group;
+        Attack task = new Attack();
+        executor.execute(task);
+        task.waitUntilDone();
+        if (!task.failedDueToSecurityException())
+            throw new RuntimeException("SecurityException expected");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/channels/AsynchronousChannelGroup/Attack.java	Fri Nov 14 12:00:55 2008 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.*;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A task that attempts to attack the current host.
+ */
+
+public class Attack implements Runnable {
+    private final CountDownLatch latch = new CountDownLatch(1);
+    private volatile boolean failedDueToSecurityException;
+
+    public void Attack() {
+        // check class is on boot class path
+        if (Attack.class.getClassLoader() != null)
+            throw new RuntimeException("Attack class not on boot class path");
+    }
+
+    @Override
+    public void run() {
+        try {
+            new Socket("127.0.0.1", 9999).close();
+            throw new RuntimeException("Connected (not expected)");
+        } catch (IOException e) {
+            throw new RuntimeException("IOException (not expected)");
+        } catch (SecurityException e) {
+            failedDueToSecurityException = true;
+        } finally {
+            latch.countDown();
+        }
+    }
+
+    public void waitUntilDone() throws InterruptedException {
+        latch.await();
+    }
+
+    public boolean failedDueToSecurityException() {
+        return failedDueToSecurityException;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java	Fri Nov 14 12:00:55 2008 +0000
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.util.concurrent.ThreadFactory;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * The "privileged" ThreadFactory used by the AsExecutor test.
+ */
+
+public class PrivilegedThreadFactory implements ThreadFactory {
+    public void PrivilegedThreadPoolFactory() {
+        // check class is on boot class path
+        if (PrivilegedThreadFactory.class.getClassLoader() != null)
+            throw new RuntimeException("PrivilegedThreadFactory class not on boot class path");
+    }
+
+    @Override
+    public Thread newThread(final Runnable r) {
+        return AccessController.doPrivileged(new PrivilegedAction<Thread>() {
+            @Override
+            public Thread run() {
+                Thread t = new Thread(r);
+                t.setDaemon(true);
+                return t;
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh	Fri Nov 14 12:00:55 2008 +0000
@@ -0,0 +1,52 @@
+#
+# Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 4607272
+# @summary Unit test for AsynchronousChannelGrou#execute
+# @build AsExecutor PrivilegedThreadFactory Attack
+# @run shell run_any_task.sh
+
+# if TESTJAVA isn't set then we assume an interactive run.
+
+if [ -z "$TESTJAVA" ]; then
+    TESTSRC=.
+    TESTCLASSES=.
+    JAVA=java
+    JAR=jar
+else
+    JAVA="${TESTJAVA}/bin/java"
+    JAR="${TESTJAVA}/bin/jar"
+fi
+
+echo "Creating JAR file ..."
+$JAR -cf "${TESTCLASSES}/Privileged.jar" \
+    -C "${TESTCLASSES}" PrivilegedThreadFactory.class \
+    -C "${TESTCLASSES}" PrivilegedThreadFactory\$1.class \
+    -C "${TESTCLASSES}" Attack.class
+
+echo "Running test ..."
+$JAVA -XX:-UseVMInterruptibleIO \
+    -Xbootclasspath/a:"${TESTCLASSES}/Privileged.jar" \
+    -classpath "${TESTCLASSES}" \
+    AsExecutor