/* * Copyright 2009 Red Hat, Inc. * * Red Hat licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.jboss.pitbull.internal.nio.socket; import org.jboss.pitbull.internal.logging.Logger; import java.io.IOException; import java.io.InterruptedIOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.SelectorProvider; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.TimeUnit; /** * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> * @author <a href="http://gleamynode.net/">Trustin Lee</a> * @version $Rev: 2200 $, $Date: 2010-02-23 14:42:39 +0900 (Tue, 23 Feb 2010) $ */ public final class SelectorUtil { protected static final Logger logger = Logger.getLogger(SelectorUtil.class); public static void safeClose(Selector selector) { try { selector.close(); } catch (Throwable ignored) {} } public static void select(Selector selector) throws IOException { try { selector.select(500); } catch (CancelledKeyException e) { // Harmless exception - log anyway logger.trace( CancelledKeyException.class.getSimpleName() + " raised by a Selector - JDK bug?", e); } } private interface SelectorCreator { Selector open() throws IOException; } private final static SelectorCreator selectorCreator; static { final String providerClassName = SelectorProvider.provider().getClass().getCanonicalName(); if ("sun.nio.ch.PollSelectorProvider".equals(providerClassName)) { logger.warn("The currently defined selector provider class (%s) is not supported for use with PitBull", providerClassName); } logger.trace("Starting up with selector provider %s", providerClassName); selectorCreator = AccessController.doPrivileged( new PrivilegedAction<SelectorCreator>() { public SelectorCreator run() { try { // A Polling selector is most efficient on most platforms for one-off selectors. Try to hack a way to get them on demand. final Class<? extends Selector> selectorImplClass = Class.forName("sun.nio.ch.PollSelectorImpl").asSubclass(Selector.class); final Constructor<? extends Selector> constructor = selectorImplClass.getDeclaredConstructor(SelectorProvider.class); // Usually package private. So untrusting. constructor.setAccessible(true); logger.trace("Using polling selector type for temporary selectors."); return new SelectorCreator() { public Selector open() throws IOException { try { return constructor.newInstance(SelectorProvider.provider()); } catch (InstantiationException e) { return Selector.open(); } catch (IllegalAccessException e) { return Selector.open(); } catch (InvocationTargetException e) { try { throw e.getTargetException(); } catch (IOException e2) { throw e2; } catch (RuntimeException e2) { throw e2; } catch (Error e2) { throw e2; } catch (Throwable t) { throw new IllegalStateException("Unexpected invocation exception", t); } } } }; } catch (Exception e) { // ignore. } // Can't get our selector type? That's OK, just use the default. logger.trace("Using default selector type for temporary selectors."); return new SelectorCreator() { public Selector open() throws IOException { return Selector.open(); } }; } } ); } private static final ThreadLocal<Selector> selectorThreadLocal = new ThreadLocal<Selector>() { public void remove() { // if no selector was created, none will be closed if (get() != null) { try { get().close(); } catch (Throwable ignored) {} } super.remove(); } }; public static void cleanupThreadSelector() { selectorThreadLocal.remove(); } private static Selector getSelector() throws IOException { final ThreadLocal<Selector> threadLocal = selectorThreadLocal; Selector selector = threadLocal.get(); if (selector == null) { selector = selectorCreator.open(); threadLocal.set(selector); } return selector; } public static void awaitReadable(SelectableChannel channel) throws IOException { await(channel, SelectionKey.OP_READ); } public static void awaitReadable(SelectableChannel channel, long time, TimeUnit unit) throws IOException { await(channel, SelectionKey.OP_READ, time, unit); } public static void awaitWritable(SelectableChannel channel) throws IOException { await(channel, SelectionKey.OP_WRITE); } public static void awaitWritable(SelectableChannel channel, long time, TimeUnit unit) throws IOException { await(channel, SelectionKey.OP_WRITE, time, unit); } public static void await(SelectableChannel channel, int op) throws IOException { final Selector selector = getSelector(); final SelectionKey selectionKey = channel.register(selector, op); selector.select(); if (Thread.currentThread().isInterrupted()) { throw new InterruptedIOException(); } selectionKey.interestOps(0); } public static void await(SelectableChannel channel, int op, long time, TimeUnit unit) throws IOException { final Selector selector = getSelector(); final SelectionKey selectionKey = channel.register(selector, op); selector.select(unit.toMillis(time)); if (Thread.currentThread().isInterrupted()) { throw new InterruptedIOException(); } selectionKey.interestOps(0); } }