/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.grizzly.aio;
import com.sun.grizzly.Controller;
import com.sun.grizzly.aio.util.AIOOutputWriter;
import com.sun.grizzly.util.ByteBufferFactory.ByteBufferType;
import com.sun.grizzly.util.DefaultThreadPool;
import com.sun.grizzly.util.ExtendedThreadPool;
import com.sun.grizzly.util.State;
import com.sun.grizzly.util.StateHolder;
import com.sun.grizzly.util.StateHolder.ConditionListener;
import java.io.IOException;
import java.nio.channels.AsynchronousChannelGroup;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Simple {@link Runnable} used to manage the lifecycle of an {@link AIOHandler}
*
* @author Jeanfrancois Arcand
*/
public class AIOHandlerRunner implements Runnable{
private static boolean useCachedThreadPool = Boolean.valueOf(
System.getProperty("com.sun.grizzly.cachedThreadPool", "false"))
.booleanValue();
private static int maxCachedThreadPoolSize = Integer.valueOf(
System.getProperty("com.sun.grizzly.cachedThreadPool.maxThreads", "50"))
.intValue();
private AIOHandler aioHandler;
private final Controller controller;
private final Logger logger;
private AsynchronousChannelGroup asyncChannelGroup;
public AIOHandlerRunner(Controller controller) {
this.controller = controller;
this.logger = Controller.logger();
}
public void run() {
int maxThreads = DefaultThreadPool.DEFAULT_MAX_THREAD_COUNT;
ExecutorService controllerThreadPool = controller.getThreadPool();
if (controllerThreadPool instanceof ExtendedThreadPool) {
maxThreads = ((ExtendedThreadPool) controllerThreadPool).
getMaximumPoolSize();
}
int corePoolThreads = maxThreads;
String threadPoolFactory = System.
getProperty("java.nio.channels.DefaultThreadPool.threadFactory");
if (threadPoolFactory == null){
System.setProperty("java.nio.channels.DefaultThreadPool.threadFactory"
,com.sun.grizzly.util.WorkerThreadFactory.class.getName());
logger.info("Swithching AIO Thread Pool to: "
+ com.sun.grizzly.util.WorkerThreadFactory.class.getName());
}
if (useCachedThreadPool){
maxThreads = maxCachedThreadPoolSize;
}
if (useCachedThreadPool && corePoolThreads > maxCachedThreadPoolSize){
logger.warning("cachedThreadPool.maxThreads cannot be lower than maxThreads: " + maxThreads
+ " .Ignoring the value");
maxThreads = corePoolThreads;
}
DefaultThreadPool threadPool = new DefaultThreadPool();
threadPool.setMaximumPoolSize(maxThreads);
threadPool.setCorePoolSize(corePoolThreads);
threadPool.setByteBufferType(ByteBufferType.DIRECT);
ThreadFactory tf = null;
if (threadPoolFactory != null){
try{
tf = (ThreadFactory)(Class.forName(threadPoolFactory,true,
Thread.currentThread().getContextClassLoader())).newInstance();
} catch (Throwable t){
Controller.logger().warning(t.getMessage());
}
threadPool.setThreadFactory(tf);
}
aioHandler.setThreadPool(threadPool);
controller.setThreadPool(threadPool);
try{
if (useCachedThreadPool){
logger.info("Using CachedThreadPool with asynchronous write set to "
+ AIOOutputWriter.ASYNC_WRITE);
asyncChannelGroup = AsynchronousChannelGroup
.withFixedThreadPool(maxThreads, tf);
} else {
logger.info("Using FixedThreadPool with asynchronous write set to "
+ AIOOutputWriter.ASYNC_WRITE);
asyncChannelGroup = AsynchronousChannelGroup
.withThreadPool(threadPool);
}
} catch (IOException ex){
logger.log(Level.SEVERE, "ThreadPoolCreation exception",ex);
}
aioHandler.setAynchronousChannelGroup(asyncChannelGroup);
StateHolder<State> controllerStateHolder = controller.getStateHolder();
StateHolder<State> selectorHandlerStateHolder = aioHandler.getStateHolder();
try {
aioHandler.start();
aioHandler.getStateHolder().setState(State.STARTED);
CountDownLatch latch = new CountDownLatch(1);
while (controllerStateHolder.getState(false) != State.STOPPED &&
selectorHandlerStateHolder.getState(false) != State.STOPPED) {
State controllerState = controllerStateHolder.getState(false);
State selectorHandlerState = selectorHandlerStateHolder.getState(false);
if (controllerState != State.PAUSED &&
selectorHandlerState != State.PAUSED) {
try {
latch.await(5, TimeUnit.SECONDS);
}catch(InterruptedException e) {
return;
}
} else {
ConditionListener<State> controllerConditionListener =
registerForNotification(controllerState,
controllerStateHolder, latch);
ConditionListener<State> selectorHandlerConditionListener =
registerForNotification(selectorHandlerState,
selectorHandlerStateHolder, latch);
try {
latch.await(5000, TimeUnit.MILLISECONDS);
} catch(InterruptedException e) {
} finally {
controllerStateHolder.removeConditionListener(controllerConditionListener);
selectorHandlerStateHolder.removeConditionListener(selectorHandlerConditionListener);
}
}
}
} finally {
aioHandler.shutdown();
controller.notifyStopped();
}
}
public void setIOHandler(AIOHandler aioHandler) {
this.aioHandler = aioHandler;
}
public AIOHandler getIOHandler() {
return aioHandler;
}
/**
* Register {@link CountDownLatch} to be notified, when either Controller
* or SelectorHandler will change their state to correspondent values
* @param currentState initial/current {@link StateHolder} state
* @param stateHolder
* @param latch
* @return <code>ConditionListener</code> if listener is registered, null if
* condition is true right now
*/
private ConditionListener<State> registerForNotification(State currentState,
StateHolder<State> stateHolder, CountDownLatch latch) {
if (currentState == State.PAUSED) {
return stateHolder.notifyWhenStateIsNotEqual(State.PAUSED, latch);
} else {
return stateHolder.notifyWhenStateIsEqual(State.STOPPED, latch);
}
}
}