/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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.apache.activemq.transport.nio; import java.io.IOException; import java.nio.channels.spi.AbstractSelectableChannel; import java.util.LinkedList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * The SelectorManager will manage one Selector and the thread that checks the * selector. * * We may need to consider running more than one thread to check the selector if * servicing the selector takes too long. */ public final class SelectorManager { public static final SelectorManager SINGLETON = new SelectorManager(); private Executor selectorExecutor = createDefaultExecutor(); private Executor channelExecutor = selectorExecutor; private final LinkedList<SelectorWorker> freeWorkers = new LinkedList<SelectorWorker>(); private int maxChannelsPerWorker = -1; protected ExecutorService createDefaultExecutor() { ThreadPoolExecutor rc = new ThreadPoolExecutor(getDefaultCorePoolSize(), getDefaultMaximumPoolSize(), getDefaultKeepAliveTime(), TimeUnit.SECONDS, newWorkQueue(), new ThreadFactory() { private long i = 0; @Override public Thread newThread(Runnable runnable) { Thread t = new Thread(runnable, "ActiveMQ NIO Worker " + (i++)); t.setDaemon(true); return t; } }, newRejectionHandler()); return rc; } private RejectedExecutionHandler newRejectionHandler() { return canRejectWork() ? new ThreadPoolExecutor.AbortPolicy() : new ThreadPoolExecutor.CallerRunsPolicy(); } private BlockingQueue<Runnable> newWorkQueue() { final int workQueueCapicity = getDefaultWorkQueueCapacity(); return workQueueCapicity > 0 ? new LinkedBlockingQueue<Runnable>(workQueueCapicity) : new SynchronousQueue<Runnable>(); } private static boolean canRejectWork() { return Boolean.getBoolean("org.apache.activemq.transport.nio.SelectorManager.rejectWork"); } private static int getDefaultWorkQueueCapacity() { return Integer.getInteger("org.apache.activemq.transport.nio.SelectorManager.workQueueCapacity", 0); } private static int getDefaultCorePoolSize() { return Integer.getInteger("org.apache.activemq.transport.nio.SelectorManager.corePoolSize", 10); } private static int getDefaultMaximumPoolSize() { return Integer.getInteger("org.apache.activemq.transport.nio.SelectorManager.maximumPoolSize", 1024); } private static int getDefaultKeepAliveTime() { return Integer.getInteger("org.apache.activemq.transport.nio.SelectorManager.keepAliveTime", 30); } private static int getDefaultMaxChannelsPerWorker() { return Integer.getInteger("org.apache.activemq.transport.nio.SelectorManager.maxChannelsPerWorker", 1024); } public static SelectorManager getInstance() { return SINGLETON; } public interface Listener { void onSelect(SelectorSelection selector); void onError(SelectorSelection selection, Throwable error); } public synchronized SelectorSelection register(AbstractSelectableChannel selectableChannel, Listener listener) throws IOException { SelectorSelection selection = null; while (selection == null) { if (freeWorkers.size() > 0) { SelectorWorker worker = freeWorkers.getFirst(); if (worker.isReleased()) { freeWorkers.remove(worker); } else { worker.retain(); selection = new SelectorSelection(worker, selectableChannel, listener); } } else { // Worker starts /w retain count of 1 SelectorWorker worker = new SelectorWorker(this); freeWorkers.addFirst(worker); selection = new SelectorSelection(worker, selectableChannel, listener); } } return selection; } synchronized void onWorkerFullEvent(SelectorWorker worker) { freeWorkers.remove(worker); } public synchronized void onWorkerEmptyEvent(SelectorWorker worker) { freeWorkers.remove(worker); } public synchronized void onWorkerNotFullEvent(SelectorWorker worker) { freeWorkers.addFirst(worker); } public Executor getChannelExecutor() { return channelExecutor; } public void setChannelExecutor(Executor channelExecutor) { this.channelExecutor = channelExecutor; } public int getMaxChannelsPerWorker() { return maxChannelsPerWorker >= 0 ? maxChannelsPerWorker : getDefaultMaxChannelsPerWorker(); } public void setMaxChannelsPerWorker(int maxChannelsPerWorker) { this.maxChannelsPerWorker = maxChannelsPerWorker; } public Executor getSelectorExecutor() { return selectorExecutor; } public void setSelectorExecutor(Executor selectorExecutor) { this.selectorExecutor = selectorExecutor; } }