All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.mina.core.service.SimpleIoProcessorPool Maven / Gradle / Ivy

There is a newer version: 3.0.0-M2
Show newest version
/*
 *  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.mina.core.service;

import java.lang.reflect.Constructor;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.session.AbstractIoSession;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.WriteRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An {@link IoProcessor} pool that distributes {@link IoSession}s into one or more
 * {@link IoProcessor}s. Most current transport implementations use this pool internally
 * to perform better in a multi-core environment, and therefore, you won't need to 
 * use this pool directly unless you are running multiple {@link IoService}s in the
 * same JVM.
 * 

* If you are running multiple {@link IoService}s, you could want to share the pool * among all services. To do so, you can create a new {@link SimpleIoProcessorPool} * instance by yourself and provide the pool as a constructor parameter when you * create the services. *

* This pool uses Java reflection API to create multiple {@link IoProcessor} instances. * It tries to instantiate the processor in the following order: *

    *
  1. A public constructor with one {@link ExecutorService} parameter.
  2. *
  3. A public constructor with one {@link Executor} parameter.
  4. *
  5. A public default constructor
  6. *
* The following is an example for the NIO socket transport: *

 * // Create a shared pool.
 * SimpleIoProcessorPool<NioSession> pool = 
 *         new SimpleIoProcessorPool<NioSession>(NioProcessor.class, 16);
 * 
 * // Create two services that share the same pool.
 * SocketAcceptor acceptor = new NioSocketAcceptor(pool);
 * SocketConnector connector = new NioSocketConnector(pool);
 * 
 * ...
 * 
 * // Release related resources.
 * connector.dispose();
 * acceptor.dispose();
 * pool.dispose();
 * 
* * @author Apache MINA Project * * @param the type of the {@link IoSession} to be managed by the specified * {@link IoProcessor}. */ public class SimpleIoProcessorPool implements IoProcessor { /** A logger for this class */ private static final Logger LOGGER = LoggerFactory.getLogger(SimpleIoProcessorPool.class); /** The default pool size, when no size is provided. */ private static final int DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + 1; /** A key used to store the processor pool in the session's Attributes */ private static final AttributeKey PROCESSOR = new AttributeKey(SimpleIoProcessorPool.class, "processor"); /** The pool table */ private final IoProcessor[] pool; /** The contained which is passed to the IoProcessor when they are created */ private final Executor executor; /** A flag set to true if we had to create an executor */ private final boolean createdExecutor; /** A lock to protect the disposal against concurrent calls */ private final Object disposalLock = new Object(); /** A flg set to true if the IoProcessor in the pool are being disposed */ private volatile boolean disposing; /** A flag set to true if all the IoProcessor contained in the pool have been disposed */ private volatile boolean disposed; /** * Creates a new instance of SimpleIoProcessorPool with a default * size of NbCPUs +1. * * @param processorType The type of IoProcessor to use */ public SimpleIoProcessorPool(Class> processorType) { this(processorType, null, DEFAULT_SIZE, null); } /** * Creates a new instance of SimpleIoProcessorPool with a defined * number of IoProcessors in the pool * * @param processorType The type of IoProcessor to use * @param size The number of IoProcessor in the pool */ public SimpleIoProcessorPool(Class> processorType, int size) { this(processorType, null, size, null); } /** * Creates a new instance of SimpleIoProcessorPool with a defined * number of IoProcessors in the pool * * @param processorType The type of IoProcessor to use * @param size The number of IoProcessor in the pool * @param selectorProvider The SelectorProvider to use */ public SimpleIoProcessorPool(Class> processorType, int size, SelectorProvider selectorProvider) { this(processorType, null, size, selectorProvider); } /** * Creates a new instance of SimpleIoProcessorPool with an executor * * @param processorType The type of IoProcessor to use * @param executor The {@link Executor} */ public SimpleIoProcessorPool(Class> processorType, Executor executor) { this(processorType, executor, DEFAULT_SIZE, null); } /** * Creates a new instance of SimpleIoProcessorPool with an executor * * @param processorType The type of IoProcessor to use * @param executor The {@link Executor} * @param size The number of IoProcessor in the pool * @param selectorProvider The SelectorProvider to used */ @SuppressWarnings("unchecked") public SimpleIoProcessorPool(Class> processorType, Executor executor, int size, SelectorProvider selectorProvider) { if (processorType == null) { throw new IllegalArgumentException("processorType"); } if (size <= 0) { throw new IllegalArgumentException("size: " + size + " (expected: positive integer)"); } // Create the executor if none is provided createdExecutor = executor == null; if (createdExecutor) { this.executor = Executors.newCachedThreadPool(); // Set a default reject handler ((ThreadPoolExecutor) this.executor).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); } else { this.executor = executor; } pool = new IoProcessor[size]; boolean success = false; Constructor> processorConstructor = null; boolean usesExecutorArg = true; try { // We create at least one processor try { try { processorConstructor = processorType.getConstructor(ExecutorService.class); pool[0] = processorConstructor.newInstance(this.executor); } catch (NoSuchMethodException e1) { // To the next step... try { if(selectorProvider==null) { processorConstructor = processorType.getConstructor(Executor.class); pool[0] = processorConstructor.newInstance(this.executor); } else { processorConstructor = processorType.getConstructor(Executor.class, SelectorProvider.class); pool[0] = processorConstructor.newInstance(this.executor,selectorProvider); } } catch (NoSuchMethodException e2) { // To the next step... try { processorConstructor = processorType.getConstructor(); usesExecutorArg = false; pool[0] = processorConstructor.newInstance(); } catch (NoSuchMethodException e3) { // To the next step... } } } } catch (RuntimeException re) { LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage()); throw re; } catch (Exception e) { String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage(); LOGGER.error(msg, e); throw new RuntimeIoException(msg, e); } if (processorConstructor == null) { // Raise an exception if no proper constructor is found. String msg = String.valueOf(processorType) + " must have a public constructor with one " + ExecutorService.class.getSimpleName() + " parameter, a public constructor with one " + Executor.class.getSimpleName() + " parameter or a public default constructor."; LOGGER.error(msg); throw new IllegalArgumentException(msg); } // Constructor found now use it for all subsequent instantiations for (int i = 1; i < pool.length; i++) { try { if (usesExecutorArg) { if(selectorProvider==null) { pool[i] = processorConstructor.newInstance(this.executor); } else { pool[i] = processorConstructor.newInstance(this.executor, selectorProvider); } } else { pool[i] = processorConstructor.newInstance(); } } catch (Exception e) { // Won't happen because it has been done previously } } success = true; } finally { if (!success) { dispose(); } } } /** * {@inheritDoc} */ @Override public final void add(S session) { getProcessor(session).add(session); } /** * {@inheritDoc} */ @Override public final void flush(S session) { getProcessor(session).flush(session); } /** * {@inheritDoc} */ @Override public final void write(S session, WriteRequest writeRequest) { getProcessor(session).write(session, writeRequest); } /** * {@inheritDoc} */ @Override public final void remove(S session) { getProcessor(session).remove(session); } /** * {@inheritDoc} */ @Override public final void updateTrafficControl(S session) { getProcessor(session).updateTrafficControl(session); } /** * {@inheritDoc} */ @Override public boolean isDisposed() { return disposed; } /** * {@inheritDoc} */ @Override public boolean isDisposing() { return disposing; } /** * {@inheritDoc} */ @Override public final void dispose() { if (disposed) { return; } synchronized (disposalLock) { if (!disposing) { disposing = true; for (IoProcessor ioProcessor : pool) { if (ioProcessor == null) { // Special case if the pool has not been initialized properly continue; } if (ioProcessor.isDisposing()) { continue; } try { ioProcessor.dispose(); } catch (Exception e) { LOGGER.warn("Failed to dispose the {} IoProcessor.", ioProcessor.getClass().getSimpleName(), e); } } if (createdExecutor) { ((ExecutorService) executor).shutdown(); } } Arrays.fill(pool, null); disposed = true; } } /** * Find the processor associated to a session. If it hasen't be stored into * the session's attributes, pick a new processor and stores it. */ @SuppressWarnings("unchecked") private IoProcessor getProcessor(S session) { IoProcessor processor = (IoProcessor) session.getAttribute(PROCESSOR); if (processor == null) { if (disposed || disposing) { throw new IllegalStateException("A disposed processor cannot be accessed."); } processor = pool[Math.abs((int) session.getId()) % pool.length]; if (processor == null) { throw new IllegalStateException("A disposed processor cannot be accessed."); } session.setAttributeIfAbsent(PROCESSOR, processor); } return processor; } }