com.sun.grizzly.http.SelectorThread Maven / Gradle / Ivy
/* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2007-2008 Sun Microsystems, Inc. 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.html * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [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.http; import com.sun.grizzly.arp.AsyncHandler; import com.sun.grizzly.Controller; import com.sun.grizzly.ControllerStateListenerAdapter; import com.sun.grizzly.DefaultProtocolChainInstanceHandler; import com.sun.grizzly.Pipeline; import com.sun.grizzly.ProtocolChain; import com.sun.grizzly.ProtocolFilter; import com.sun.grizzly.TCPSelectorHandler; import com.sun.grizzly.arp.AsyncProtocolFilter; import com.sun.grizzly.filter.ReadFilter; import com.sun.grizzly.http.algorithms.NoParsingAlgorithm; import com.sun.grizzly.http.FileCache.FileCacheEntry; import com.sun.grizzly.portunif.PUReadFilter; import com.sun.grizzly.portunif.PUPreProcessor; import com.sun.grizzly.portunif.ProtocolFinder; import com.sun.grizzly.portunif.ProtocolHandler; import com.sun.grizzly.rcm.ResourceAllocationFilter; import com.sun.grizzly.util.SelectorFactory; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.util.Iterator; import java.util.logging.Logger; import java.util.logging.Level; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentHashMap; import java.nio.channels.Selector; import java.nio.channels.SelectionKey; import com.sun.grizzly.tcp.Adapter; import com.sun.grizzly.tcp.RequestGroupInfo; import com.sun.grizzly.util.IntrospectionUtils; import com.sun.grizzly.util.LoggerUtils; import com.sun.grizzly.util.res.StringManager; import java.io.File; import javax.management.ObjectName; import javax.management.MBeanServer; import javax.management.MBeanRegistration; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; /** * The SelectorThread class is the entry point when embedding the Grizzly Web * Server. All Web Server configuration must be set on this object before invoking * the {@link listen()} method. As an example: *
. */ protected LinkedListPipeline processorPipeline; /** * Placeholder for {@link Pipeline} statistic. */ protected PipelineStatistic pipelineStat; /** * The default {@link Pipeline} used. */ protected String pipelineClassName = com.sun.grizzly.http.LinkedListPipeline.class.getName(); /** * Maximum number of {@link WorkerThread} */ protected int maxThreads = 5; // By default /** * Minimum numbers of {@link WorkerThread} created */ protected int minWorkerThreads = 5; /** * The number used when increamenting the {@link Pipeline} * thread pool. */ protected int threadsIncrement = 1; /** * The timeout used by the thread when processing a request. */ protected int threadsTimeout = Constants.DEFAULT_TIMEOUT; /** * Is the {@link ByteBuffer} used by the* * @author Jean-Francois Arcand */ public class SelectorThread extends Thread implements MBeanRegistration{ public final static String SERVER_NAME = System.getProperty("product.name") != null ? System.getProperty("product.name") : "grizzly"; private Object[] lock = new Object[0]; protected int serverTimeout = Constants.DEFAULT_SERVER_SOCKET_TIMEOUT; protected InetAddress inet; protected int port; protected boolean initialized = false; protected volatile boolean running = false; // ----------------------------------------------------- JMX Support ---/ protected String domain; protected ObjectName oname; protected ObjectName globalRequestProcessorName; private ObjectName keepAliveMbeanName; private ObjectName connectionQueueMbeanName; private ObjectName fileCacheMbeanName; protected MBeanServer mserver; protected ObjectName processorWorkerThreadName; // ------------------------------------------------------Socket setting --/ protected boolean tcpNoDelay=false; protected int linger=100; protected int socketTimeout=-1; protected int maxKeepAliveRequests = Constants.DEFAULT_MAX_KEEP_ALIVE; // Number of keep-alive threads protected int keepAliveThreadCount = 1; // ------------------------------------------------------ Compression ---/ /** * Compression value. */ protected String compression = "off"; protected String noCompressionUserAgents = null; protected String restrictedUserAgents = null; protected String compressableMimeTypes = "text/html,text/xml,text/plain"; private volatile String[] parsedCompressableMimeTypes = null; private volatile int parsedComressableMimeTypesHash = -1; protected int compressionMinSize = 2048; // ------------------------------------------------------ Properties----/ /** * Is the socket reuse socket enabled. */ private boolean reuseAddress = true; /** * Buffer the response until the buffer is full. */ protected boolean bufferResponse = false; /** * Default HTTP header buffer size. */ protected int maxHttpHeaderSize = Constants.DEFAULT_HEADER_SIZE; protected int maxPostSize = 2 * 1024 * 1024; /** * The {@link Selector} used by the connector. */ protected Selector selector; /** * {@link SelectorHandler} current {@link SelectorThread} is * based on */ protected TCPSelectorHandler selectorHandler; /** * Associated adapter. */ protected Adapter adapter = null; /** * The queue shared by this thread and the code>ProcessorTaskfinal SelectorThread selectorThread = new SelectorThread(){ public void listen() throws IOException, InstantiationException{ super.listen(); System.out.println("Server started in " + (System.currentTimeMillis() - t1) + " milliseconds."); } }; selectorThread.setAlgorithmClassName(StaticStreamAlgorithm.class.getName()); selectorThread.setPort(port); SelectorThread.setWebAppRootPath(folder); Adapter adapter = new StaticResourcesAdapter(folder); ((StaticResourcesAdapter)adapter).setRootFolder(folder); selectorThread.setAdapter(adapter); selectorThread.setDisplayConfiguration(true); selectorThread.listen(); *
ReadTask
use * direct {@link ByteBuffer} or not. */ protected boolean useDirectByteBuffer = false; /** * Monitoring object used to store information. */ protected RequestGroupInfo globalRequestProcessor= new RequestGroupInfo(); /** * Keep-alive stats */ private KeepAliveStats keepAliveStats = new KeepAliveStats(); /** * If true, display the NIO configuration information. */ protected boolean displayConfiguration = false; /** * Is monitoring already started. */ protected boolean isMonitoringEnabled = false; /** * The input request buffer size. */ protected int requestBufferSize = Constants.DEFAULT_REQUEST_BUFFER_SIZE; /** * Create view {@link ByteBuffer} from another {@link ByteBuffer} */ protected boolean useByteBufferView = false; /* * Number of seconds before idle keep-alive connections expire */ protected int keepAliveTimeoutInSeconds = Constants.DEFAULT_TIMEOUT; /** * Recycle the {@link Task} after running them */ protected boolean recycleTasks = Constants.DEFAULT_RECYCLE; /** * The {@link Selector} timeout value. By default, it is set to 60000 * miliseconds (as in the j2se 1.5 ORB). */ protected static int selectorTimeout = 1000; /** * Maximum pending connection before refusing requests. */ protected int maxQueueSizeInBytes = Constants.DEFAULT_QUEUE_SIZE; /** * The{@link Algorithm} used to predict the end of the NIO stream */ protected Class algorithmClass; /** * The{@link Algorithm} used to parse the NIO stream. */ protected String algorithmClassName = DEFAULT_ALGORITHM; /** * The default NIO stream algorithm. */ public final static String DEFAULT_ALGORITHM = com.sun.grizzly.http.algorithms. NoParsingAlgorithm.class.getName(); /** * Server socket backlog. */ protected int ssBackLog = 4096; /** * The default response-type */ protected String defaultResponseType = Constants.DEFAULT_RESPONSE_TYPE; /** * The forced request-type */ protected String forcedRequestType = Constants.FORCED_REQUEST_TYPE; /** * The root folder where application are deployed */ protected static String rootFolder = ""; /** * The Grizzly's Controller. */ protected Controller controller; /** * RCM support */ protected boolean rcmSupport = false; /** * Port unification filter */ protected PUReadFilter portUnificationFilter; protected boolean oOBInline = false; /** * Holder for our configured properties. */ protected HashMapproperties = new HashMap (); // ---------------------------------------------------- Object pools --// /** * {@link ConcurrentLinkedQueue} used as an object pool. * If the list becomes empty, new {@link ProcessorTask} will be * automatically added to the list. */ protected ConcurrentLinkedQueue processorTasks = new ConcurrentLinkedQueue (); /** * List of active {@link ProcessorTask}. */ protected ConcurrentLinkedQueue activeProcessorTasks = new ConcurrentLinkedQueue (); // ----------------------------------------- Multi-Selector supports --// /** * The number of {@link SelectorReadThread} */ protected int readThreadsCount = 0; /** * The logger used by the grizzly classes. */ protected static Logger logger = LoggerUtils.getLogger(); /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(Constants.Package); /** * Flag to disable setting a different time-out on uploads. */ protected boolean disableUploadTimeout = true; /** * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server. */ protected int uploadTimeout = 30000; // ----------------------------------------- Keep-Alive subsystems --// /** * Keep-Alive subsystem. If a client opens a socket but never close it, * the {@link SelectionKey} will stay forever in the * {@link Selector} keys, and this will eventualy produce a * memory leak. */ protected KeepAliveCountManager keepAliveCounter; // ------------------------------------------------- FileCache support --// /** * The FileCacheFactory associated with this Selector */ protected FileCacheFactory fileCacheFactory; /** * Timeout before remove the static resource from the cache. */ protected int secondsMaxAge = -1; /** * The maximum entries in the {@link FileCache} */ protected int maxCacheEntries = 1024; /** * The maximum size of a cached resources. */ protected long minEntrySize = 2048; /** * The maximum size of a cached resources. */ protected long maxEntrySize = 537600; /** * The maximum cached bytes */ protected long maxLargeFileCacheSize = 10485760; /** * The maximum cached bytes */ protected long maxSmallFileCacheSize = 1048576; /** * Is the FileCache enabled. */ protected boolean isFileCacheEnabled = true; /** * Is the large FileCache enabled. */ protected boolean isLargeFileCacheEnabled = true; // --------------------------------------------- Asynch supports -----// /** * Is asynchronous mode enabled? */ protected boolean asyncExecution = false; /** * When the asynchronous mode is enabled, the execution of this object * will be delegated to the {@link AsyncHandler} */ protected AsyncHandler asyncHandler; /** * Is the DEFAULT_ALGORITHM used. */ protected static boolean defaultAlgorithmInstalled = true; /** * The JMX Management class. */ private Management jmxManagement = null; /** * The Classloader used to load instance of StreamAlgorithm. */ private ClassLoader classLoader; /** * Grizzly own debug flag. */ protected boolean enableNioLogging = false; /** * Static list of current instance of this class. */ private final static ConcurrentHashMap selectorThreads = new ConcurrentHashMap (); /** * The current SelectionKeyHandler. */ private SelectorThreadKeyHandler keyHandler; // ---------------------------------------------------- Constructor --// /** * Create the {@link Selector} object. Each instance of this class * will listen to a specific port. */ public SelectorThread(){ } // ------------------------------------------------------ Selector hook --/ /** * Return the {@link SelectorThread} which listen on port, or null * if there is no {@link SelectorThread}. */ public final static SelectorThread getSelector(int port){ return selectorThreads.get(port); } /** * Return an Enumeration
of the active * {@link SelectorThread}s */ public final static EnumerationgetSelectors(){ return selectorThreads.elements(); } // ----------------------------------------------------------------------/ /** * Register a {@link SelectionKey} to this {@link Selector} * running of this thread. */ public void registerKey(SelectionKey key){ selectorHandler.register(key, SelectionKey.OP_READ); } // -------------------------------------------------------------- Init // /** * Initialize the Grizzly Framework classes. */ protected void initController(){ if (controller == null){ controller = new Controller(); } // Set the logger of the utils package to the same as this class. LoggerUtils.setLogger(logger); // Set the controller logger Controller.setLogger(logger); selectorHandler = createSelectorHandler(); configureSelectorHandler(selectorHandler); controller.setSelectorHandler(selectorHandler); keyHandler = new SelectorThreadKeyHandler(this); keyHandler.setLogger(logger); keyHandler.setTimeout(keepAliveTimeoutInSeconds * 1000); selectorHandler.setSelectionKeyHandler(keyHandler); final HttpProtocolChain protocolChain = new HttpProtocolChain(); protocolChain.enableRCM(rcmSupport); configureFilters(protocolChain); DefaultProtocolChainInstanceHandler instanceHandler = new DefaultProtocolChainInstanceHandler(){ /** * Always return instance of ProtocolChain. */ @Override public ProtocolChain poll(){ return protocolChain; } /** * Pool an instance of ProtocolChain. */ @Override public boolean offer(ProtocolChain instance){ return true; } }; controller.setProtocolChainInstanceHandler(instanceHandler); controller.setPipeline(processorPipeline); controller.setReadThreadsCount(readThreadsCount); } /** * Create {@link TCPSelectorHandler} */ protected TCPSelectorHandler createSelectorHandler() { return new SelectorThreadHandler(this); } /** * Configure {@link TCPSelectorHandler} */ protected void configureSelectorHandler(TCPSelectorHandler selectorHandler) { selectorHandler.setSelector(selector); selectorHandler.setPort(port); selectorHandler.setInet(inet); selectorHandler.setLinger(linger); selectorHandler.setLogger(logger); selectorHandler.setReuseAddress(reuseAddress); selectorHandler.setSelectTimeout(selectorTimeout); selectorHandler.setServerTimeout(serverTimeout); selectorHandler.setSocketTimeout(keepAliveTimeoutInSeconds * 1000); selectorHandler.setSsBackLog(ssBackLog); selectorHandler.setTcpNoDelay(tcpNoDelay); } /** * Create and configure resource allocation {@link ProtocolFilter} * @return resource allocation {@link ProtocolFilter} */ protected ProtocolFilter createRaFilter() { return new ResourceAllocationFilter() { /** * Creates a new {@link Pipeline} */ @Override protected Pipeline newPipeline(int threadCount, Pipeline p) { if (threadCount == 0) { return null; } Pipeline pipeline = new LinkedListPipeline(); pipeline.setMinThreads(1); pipeline.setMaxThreads(threadCount); pipeline.setName("RCM_" + threadCount); pipeline.initPipeline(); pipeline.startPipeline(); return pipeline; } }; } /** * Create HTTP parser {@link ProtocolFilter} * @return HTTP parser {@link ProtocolFilter} */ protected ProtocolFilter createHttpParserFilter() { if (asyncExecution){ return new AsyncProtocolFilter(algorithmClass,port); } else { return new DefaultProtocolFilter(algorithmClass, port); } } /** * Adds and configures {@link ProtocolChain}'s filters * @param {@link ProtocolChain} to configure */ protected void configureFilters(ProtocolChain protocolChain) { if (portUnificationFilter != null) { protocolChain.addFilter(portUnificationFilter); } else if (rcmSupport) { protocolChain.addFilter(createRaFilter()); } else { ReadFilter readFilter = new ReadFilter(); protocolChain.addFilter(readFilter); } protocolChain.addFilter(createHttpParserFilter()); } /** * Configures port unification depending on passed Properties
* @param propsProperties
. If props is null - port unification * will be configured from System properties */ public void configurePortUnification(Properties props) { if (props == null) { props = System.getProperties(); } portUnificationFilter = new PUReadFilter(); portUnificationFilter.configure(props); } /** * Configures port unification depending on passedList
s * @param protocolFinders {@link ProtocolFinder}s * @param protocolHandlers {@link ProtocolHandler}s * @param preProcessorsPUPreProcessor
s */ public void configurePortUnification(ListprotocolFinders, List protocolHandlers, List preProcessors) { portUnificationFilter = new PUReadFilter(); portUnificationFilter.configure(protocolFinders, protocolHandlers, preProcessors); } /** * Create a new {@link Pipeline} instance using the * pipelineClassName
value. */ protected LinkedListPipeline newPipeline(int maxThreads, int minThreads, String name, int port, int priority){ Class className = null; LinkedListPipeline pipeline = null; try{ if ( classLoader == null ){ className = Class.forName(pipelineClassName); } else { className = classLoader.loadClass(pipelineClassName); } pipeline = (LinkedListPipeline)className.newInstance(); } catch (ClassNotFoundException ex){ logger.log(Level.WARNING, "Unable to load Pipeline: " + pipelineClassName); pipeline = new LinkedListPipeline(); } catch (InstantiationException ex){ logger.log(Level.WARNING, "Unable to instantiate Pipeline: " + pipelineClassName); pipeline = new LinkedListPipeline(); } catch (IllegalAccessException ex){ logger.log(Level.WARNING, "Unable to instantiate Pipeline: " + pipelineClassName); pipeline = new LinkedListPipeline(); } if (logger.isLoggable(Level.FINE)){ logger.log(Level.FINE, "http-listener " + port + " uses pipeline: " + pipeline.getClass().getName()); } pipeline.setMaxThreads(maxThreads); pipeline.setMinThreads(minThreads); pipeline.setName(name); pipeline.setPort(port); pipeline.setPriority(priority); pipeline.setMaxQueueSize(maxQueueSizeInBytes); pipeline.setThreadsIncrement(threadsIncrement); return pipeline; } /** * Initialize the {@link FileCacheFactory} associated with this instance */ protected void initFileCacheFactory(){ if (fileCacheFactory != null) return; fileCacheFactory = FileCacheFactory.getFactory(port); FileCacheFactory.setIsEnabled(isFileCacheEnabled); fileCacheFactory.setLargeFileCacheEnabled(isLargeFileCacheEnabled); fileCacheFactory.setSecondsMaxAge(secondsMaxAge); fileCacheFactory.setMaxCacheEntries(maxCacheEntries); fileCacheFactory.setMinEntrySize(minEntrySize); fileCacheFactory.setMaxEntrySize(maxEntrySize); fileCacheFactory.setMaxLargeCacheSize(maxLargeFileCacheSize); fileCacheFactory.setMaxSmallCacheSize(maxSmallFileCacheSize); fileCacheFactory.setIsMonitoringEnabled(isMonitoringEnabled); fileCacheFactory.setHeaderBBSize(requestBufferSize); } /** * Injects {@link PipelineStatistic} into every * {@link Pipeline}, for monitoring purposes. */ protected void enablePipelineStats(){ pipelineStat.start(); processorPipeline.setPipelineStatistic(pipelineStat); pipelineStat.setProcessorPipeline(processorPipeline); if (keepAliveCounter != null){ keepAliveCounter.setKeepAliveStats(keepAliveStats); } } /** * Removes {@link PipelineStatistic} from every * {@link Pipeline}, when monitoring has been turned off. */ protected void disablePipelineStats(){ pipelineStat.stop(); processorPipeline.setPipelineStatistic(null); pipelineStat.setProcessorPipeline(null); if (keepAliveCounter != null){ keepAliveCounter.setKeepAliveStats(null); } } /** * Load using reflection the{@link Algorithm} class. */ protected void initAlgorithm(){ try{ if (classLoader == null){ algorithmClass = Class.forName(algorithmClassName); } else { algorithmClass = classLoader.loadClass(algorithmClassName); } logger.log(Level.FINE, "Using Algorithm: " + algorithmClassName); } catch (ClassNotFoundException ex){ logger.log(Level.FINE, "Unable to load Algorithm: " + algorithmClassName); } finally { if ( algorithmClass == null ){ algorithmClass = NoParsingAlgorithm.class; } } defaultAlgorithmInstalled = algorithmClassName.equals(DEFAULT_ALGORITHM) ? true:false; } /** * Initialize the keep-alive mechanism. */ protected void initKeepAliveCounter(){ keepAliveCounter = new KeepAliveCountManager(); keepAliveCounter.setMaxKeepAliveRequests(maxKeepAliveRequests); keepAliveCounter .setKeepAliveTimeoutInSeconds(keepAliveTimeoutInSeconds); keepAliveCounter.setPort(port); keepAliveCounter.setThreadsTimeout(threadsTimeout); keepAliveCounter.setMaxThreads(keepAliveThreadCount); keepAliveStats.setMaxConnections(maxKeepAliveRequests); keepAliveStats.setSecondsTimeouts(keepAliveTimeoutInSeconds); } /** * Init the {@link Pipeline}s used by the {@link WorkerThread}s. */ protected void initPipeline(){ if (processorPipeline != null) return; selectorThreads.put(port,this); processorPipeline = newPipeline(maxThreads, minWorkerThreads, "http", port,Thread.MAX_PRIORITY); } /** * Create a pool of {@link ProcessorTask} */ protected void initProcessorTask(int size){ for (int i=0; i < size; i++){ processorTasks.offer(newProcessorTask(false)); } } /** * Initialize {@link ProcessorTask} */ protected void rampUpProcessorTask(){ Iteratoriterator = processorTasks.iterator(); while (iterator.hasNext()) { iterator.next().initialize(); } } /** * Create {@link ProcessorTask} objects and configure it to be ready * to proceed request. */ protected ProcessorTask newProcessorTask(boolean initialize){ DefaultProcessorTask task = new DefaultProcessorTask(initialize, bufferResponse); return configureProcessorTask(task); } protected ProcessorTask configureProcessorTask(DefaultProcessorTask task){ task.setAdapter(adapter); task.setMaxHttpHeaderSize(maxHttpHeaderSize); task.setBufferSize(requestBufferSize); task.setSelectorThread(this); task.setRecycle(recycleTasks); task.setDefaultResponseType(defaultResponseType); task.setForcedRequestType(forcedRequestType); task.setMaxPostSize(maxPostSize); task.setTimeout(uploadTimeout); task.setDisableUploadTimeout(disableUploadTimeout); if ( keepAliveCounter.dropConnection() ) { task.setDropConnection(true); } // Asynch extentions if ( asyncExecution ) { task.setEnableAsyncExecution(asyncExecution); task.setAsyncHandler(asyncHandler); } task.setPipeline(processorPipeline); configureCompression(task); return (ProcessorTask)task; } /** * Reconfigure Grizzly Asynchronous Request Processing(ARP) internal * objects. */ protected void reconfigureAsyncExecution(){ for(ProcessorTask task :processorTasks){ if (task instanceof DefaultProcessorTask) { ((DefaultProcessorTask)task) .setEnableAsyncExecution(asyncExecution); ((DefaultProcessorTask)task).setAsyncHandler(asyncHandler); } } } /** * Return a {@link ProcessorTask} from the pool. If the pool is empty, * create a new instance. */ public ProcessorTask getProcessorTask(){ ProcessorTask processorTask = null; if (recycleTasks) { processorTask = processorTasks.poll(); } if (processorTask == null){ processorTask = newProcessorTask(false); } if ( isMonitoringEnabled() ){ activeProcessorTasks.offer(processorTask); } return processorTask; } // --------------------------------------------------------- Thread run --/ /** * Start the endpoint (this) */ @Override public void run(){ try{ running = true; rampUpProcessorTask(); registerComponents(); displayConfiguration(); startListener(); } catch (Exception ex){ logger.log(Level.SEVERE, sm.getString("selectorThread.errorOnRequest"), ex); } } // ---------------------------------------------------Endpoint Lifecycle --/ /** * initialized the endpoint by creating the ServerScoketChannel
* and by initializing the server socket. */ public void initEndpoint() throws IOException, InstantiationException { SelectorThreadConfig.configure(this); configureProperties(); initAlgorithm(); initKeepAliveCounter(); initPipeline(); initController(); initFileCacheFactory(); initMonitoringLevel(); setName("SelectorThread-" + port); initProcessorTask(maxThreads); SelectorFactory.setMaxSelectors(maxThreads); initialized = true; } public void stopEndpoint() { if (!running) return; running = false; try{ controller.stop(); } catch (IOException ex){ logger.log(Level.WARNING, sm.getString("stopException"),ex); } synchronized(lock){ // Wait for the main thread to stop. clearTasks(); } } /** * Use reflection to configure Grizzly setter. */ private void configureProperties(){ Iterator keys = properties.keySet().iterator(); while( keys.hasNext() ) { String name = (String)keys.next(); String value = properties.get(name).toString(); IntrospectionUtils.setProperty(this, name, value); } } /** * Start the Acceptor Thread and wait for incoming connection, in a non * blocking mode. */ public void startEndpoint() throws IOException, InstantiationException { run(); // For compatibility with grizzly 1.0 } /** * Start the SelectorThread using its own thread and don't block the Thread. * This method should be used when Grizzly is embedded. */ public void listen() throws IOException, InstantiationException{ initEndpoint(); final CountDownLatch latch = new CountDownLatch(1); controller.addStateListener(new ControllerStateListenerAdapter() { @Override public void onReady() { latch.countDown(); } @Override public void onException(Throwable e) { if (latch.getCount() > 0) { logger().log(Level.SEVERE, "Exception during " + "starting the controller", e); latch.countDown(); } else { logger().log(Level.SEVERE, "Exception during " + "controller processing", e); } } }); super.start(); try { latch.await(); } catch (InterruptedException ex) { } if (!controller.isStarted()) { throw new IllegalStateException("Controller is not started!"); } } /** * Starts the {@link Pipeline} used by this {@link Selector} */ protected void startPipelines(){ processorPipeline.startPipeline(); } /** * Stop the {@link Pipeline} used by this {@link Selector} */ protected void stopPipelines(){ if ( keepAliveCounter != null ) keepAliveCounter.stopPipeline(); processorPipeline.stopPipeline(); } /** * Start a non blocking {@link Selector} object. */ protected void startListener(){ synchronized(lock){ try{ controller.start(); stopPipelines(); unregisterComponents(); } catch (Throwable t){ logger.log(Level.SEVERE, sm.getString("selectorThread.stopException"),t); } } } /** * Cancel the current {@link SelectionKey} */ public void cancelKey(SelectionKey key){ selectorHandler.getSelectionKeyHandler().cancel(key); } /** * Returns the {@link Task} object to the pool. */ public void returnTask(Task task){ // Returns the object to the pool. if (task != null) { if (task.getType() == Task.PROCESSOR_TASK){ if ( isMonitoringEnabled() ){ activeProcessorTasks.remove(((DefaultProcessorTask)task)); } processorTasks.offer((DefaultProcessorTask)task); } } } /** * Clear all cachedTasks
*/ protected void clearTasks(){ processorTasks.clear(); } // ------------------------------------------------------Public methods--/ public void setMaxThreads(int maxThreads) { if ( maxThreads == 1 ) { maxThreads = 5; } else { this.maxThreads = maxThreads; } } public int getMaxThreads() { return maxThreads; } /** * Similar togetPort()
, but getting port number directly from * connection ({@link ServerSocket}, {@link DatagramSocket}). * So if default port number 0 was set during initialization, thengetPort()
* will return 0, but getPortLowLevel() will return port number assigned by OS. * * @return port number, or -1 if {@link SelectorThread} was not started */ public int getPortLowLevel() { return selectorHandler.getPortLowLevel(); } public int getPort() { return port; } public void setPort(int port ) { this.port=port; } public InetAddress getAddress() { return inet; } public void setAddress(InetAddress inet) { this.inet=inet; } public boolean isRunning() { return running; } /** * Provides the count of request threads that are currently * being processed by the container * * @return The count of busy threads */ public int getCurrentBusyProcessorThreads() { int busy = processorPipeline.getCurrentThreadsBusy(); return busy; } /** * Sets the timeout in ms of the server sockets created by this * server. This method allows the developer to make servers * more or less responsive to having their server sockets * shut down. * *By default this value is 1000ms. */ public void setServerTimeout(int timeout) { this.serverTimeout = timeout; } public boolean getTcpNoDelay() { return tcpNoDelay; } public void setTcpNoDelay( boolean b ) { tcpNoDelay=b; } public int getLinger() { return linger; } public void setLinger( int i ) { linger=i; } public int getServerTimeout() { return serverTimeout; } public InetAddress getInet() { return inet; } public void setInet(InetAddress inet) { this.inet = inet; } public int getSocketTimeout() { return socketTimeout; } public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } // ------------------------------------------------------ Connector Methods /** * Get the maximum pending connection this {@link Pipeline} * can handle. */ public int getQueueSizeInBytes(){ return maxQueueSizeInBytes; } public int getMaxKeepAliveRequests() { return maxKeepAliveRequests; } /** * Set the maximum number of Keep-Alive requests that we will honor. */ public void setMaxKeepAliveRequests(int mkar) { maxKeepAliveRequests = mkar; } /** * Sets the number of seconds before a keep-alive connection that has * been idle times out and is closed. * * @param timeout Keep-alive timeout in number of seconds */ public void setKeepAliveTimeoutInSeconds(int timeout) { keepAliveTimeoutInSeconds = timeout; if (keepAliveStats != null) { keepAliveStats.setSecondsTimeouts(timeout); } if (keepAliveCounter != null) { keepAliveCounter.setKeepAliveTimeoutInSeconds(timeout); } if (keyHandler != null) { keyHandler.setTimeout(timeout * 1000); } } /** * Gets the number of seconds before a keep-alive connection that has * been idle times out and is closed. * * @return Keep-alive timeout in number of seconds */ public int getKeepAliveTimeoutInSeconds() { return keepAliveTimeoutInSeconds; } /** * Gets the number of keep-alive threads. * * @return number of keep-alive threads */ public int getKeepAliveThreadCount() { return keepAliveThreadCount; } /** * Sets the number of keep-alive threads. * * @param threadCount Number of keep-alive threads */ public void setKeepAliveThreadCount(int threadCount) { if (keepAliveCounter != null) { keepAliveCounter.setMaxThreads(threadCount); } keepAliveThreadCount = threadCount; } /** * Set the associated adapter. * * @param adapter the new adapter */ public void setAdapter(Adapter adapter) { this.adapter = adapter; } /** * Get the associated adapter. * * @return the associated adapter */ public Adapter getAdapter() { return adapter; } protected void setSocketOptions(Socket socket){ try{ if(linger >= 0 ) { socket.setSoLinger( true, linger); } } catch (SocketException ex){ logger.log(Level.WARNING, "setSoLinger exception ",ex); } try{ if( tcpNoDelay ) socket.setTcpNoDelay(tcpNoDelay); } catch (SocketException ex){ logger.log(Level.WARNING, "setTcpNoDelay exception ",ex); } try{ socket.setReuseAddress(reuseAddress); } catch (SocketException ex){ logger.log(Level.WARNING, "setReuseAddress exception ",ex); } try{ if(oOBInline){ socket.setOOBInline(oOBInline); } } catch (SocketException ex){ logger.log(Level.WARNING, "setOOBInline exception ",ex); } } // ------------------------------- JMX and Monnitoring support --------// public ObjectName getObjectName() { return oname; } public String getDomain() { return domain; } public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { oname=name; mserver=server; domain=name.getDomain(); return name; } public void postRegister(Boolean registrationDone) { // Do nothing } public void preDeregister() throws Exception { // Do nothing } public void postDeregister() { // Do nothing } /** * Register JMX components supported by this {@link SelectorThread}. This * include {@link FileCache}, {@link RequestInfo}, {@link KeepAliveCountManager} * and {@link LinkedListPipeline}. The {@link Management#registerComponent} * will be invoked during the registration process. */ public void registerComponents(){ if( this.domain != null && jmxManagement != null) { try { globalRequestProcessorName = new ObjectName( domain + ":type=GlobalRequestProcessor,name=GrizzlyHttpEngine-" + port); jmxManagement.registerComponent(globalRequestProcessor, globalRequestProcessorName, null); keepAliveMbeanName = new ObjectName( domain + ":type=KeepAlive,name=GrizzlyHttpEngine-" + port); jmxManagement.registerComponent(keepAliveStats, keepAliveMbeanName, null); connectionQueueMbeanName = new ObjectName( domain + ":type=ConnectionQueue,name=GrizzlyHttpEngine-" + port); jmxManagement.registerComponent(pipelineStat, connectionQueueMbeanName, null); fileCacheMbeanName = new ObjectName( domain + ":type=FileCache,name=GrizzlyHttpEngine-" + port); jmxManagement.registerComponent(fileCacheFactory, fileCacheMbeanName, null); } catch (Exception ex) { logger.log(Level.WARNING, sm.getString("selectorThread.mbeanRegistrationException"), new Object[]{new Integer(port),ex}); } } } /** * Unregister JMX components supported by this {@link SelectorThread}. This * include {@link FileCache}, {@link RequestInfo}, {@link KeepAliveCountManager} * , {@link LinkedListPipeline} and {@link DefaultProcessorTask}. The {@link Management#unregisterComponent} * will be invoked during the registration process. */ protected void unregisterComponents(){ if (this.domain != null && jmxManagement != null) { try { if (globalRequestProcessorName != null) { jmxManagement.unregisterComponent(globalRequestProcessorName); } if (keepAliveMbeanName != null) { jmxManagement.unregisterComponent(keepAliveMbeanName); } if (connectionQueueMbeanName != null) { jmxManagement.unregisterComponent(connectionQueueMbeanName); } if (fileCacheMbeanName != null) { jmxManagement.unregisterComponent(fileCacheMbeanName); } } catch (Exception ex) { logger.log(Level.WARNING, sm.getString("mbeanDeregistrationException"), new Object[]{new Integer(port),ex}); } Iterator
iterator = activeProcessorTasks.iterator(); ProcessorTask pt = null; while (iterator.hasNext()) { pt = iterator.next(); if (pt instanceof DefaultProcessorTask) { ((DefaultProcessorTask)pt).unregisterMonitoring(); } } iterator = processorTasks.iterator(); pt = null; while (iterator.hasNext()) { pt = iterator.next(); if (pt instanceof DefaultProcessorTask) { ((DefaultProcessorTask)pt).unregisterMonitoring(); } } } } /** * Return the {@link Management} interface, or null if JMX management is * no enabled. * @return the {@link Management} */ public Management getManagement() { return jmxManagement; } /** * Set the {@link Management} interface. Setting this interface automatically * expose Grizzl Http Engine mbeans. * @param jmxManagement */ public void setManagement(Management jmxManagement) { this.jmxManagement = jmxManagement; } /** * Enable gathering of monitoring data. */ public void enableMonitoring(){ if (pipelineStat == null){ initPipeline(); initMonitoringLevel(); initFileCacheFactory(); } isMonitoringEnabled = true; enablePipelineStats(); fileCacheFactory.setIsMonitoringEnabled(isMonitoringEnabled); } /** * Disable gathering of monitoring data. */ public void disableMonitoring(){ disablePipelineStats(); fileCacheFactory.setIsMonitoringEnabled(isMonitoringEnabled); } /** * Returns true if monitoring has been enabled, * false otherwise. */ public boolean isMonitoringEnabled() { return isMonitoringEnabled; } public RequestGroupInfo getRequestGroupInfo() { return globalRequestProcessor; } public KeepAliveStats getKeepAliveStats() { return keepAliveStats; } /** * Initialize the {@link PipelineStat} instance. */ protected void initMonitoringLevel() { if (pipelineStat != null) return; pipelineStat = new PipelineStatistic(port); pipelineStat.setQueueSizeInBytes(maxQueueSizeInBytes); } // ------------------------------------------- Config ------------------// public int getMaxHttpHeaderSize() { return maxHttpHeaderSize; } public void setMaxHttpHeaderSize(int maxHttpHeaderSize) { this.maxHttpHeaderSize = maxHttpHeaderSize; } /** * The minimun threads created at startup. */ public void setMinThreads(int minWorkerThreads){ this.minWorkerThreads = minWorkerThreads; } /** * Set the request input buffer size */ public void setBufferSize(int requestBufferSize){ this.requestBufferSize = requestBufferSize; } /** * Return the request input buffer size */ public int getBufferSize(){ return requestBufferSize; } // --------------------------------------------------- Low level NIO ----// public Selector getSelector(){ if (selector != null) { return selector; } else { selectorHandler.getSelector(); } return null; } public Controller getController() { return controller; } public void setController(Controller controller) { this.controller = controller; } //------------------------------------------------- FileCache config -----/ /** * Remove a context path from the {@link FileCache}. */ public void removeCacheEntry(String contextPath){ ConcurrentHashMap cachedEntries = fileCacheFactory.getCache(); if ( cachedEntries == null){ return; } Iterator iterator = cachedEntries.keySet().iterator(); String cachedPath; while (iterator.hasNext()){ cachedPath = iterator.next(); if ( cachedPath.startsWith(contextPath) ){ cachedEntries.remove(cachedPath).run(); } } } /** * The timeout in seconds before remove a {@link FileCacheEntry} * from the {@link FileCache} */ public void setSecondsMaxAge(int sMaxAges){ secondsMaxAge = sMaxAges; } /** * Set the maximum entries this cache can contains. */ public void setMaxCacheEntries(int mEntries){ maxCacheEntries = mEntries; } /** * Return the maximum entries this cache can contains. */ public int getMaxCacheEntries(){ return maxCacheEntries; } /** * Set the maximum size a {@link FileCacheEntry} can have. */ public void setMinEntrySize(long mSize){ minEntrySize = mSize; } /** * Get the maximum size a {@link FileCacheEntry} can have. */ public long getMinEntrySize(){ return minEntrySize; } /** * Set the maximum size a {@link FileCacheEntry} can have. */ public void setMaxEntrySize(long mEntrySize){ maxEntrySize = mEntrySize; } /** * Get the maximum size a {@link FileCacheEntry} can have. */ public long getMaxEntrySize(){ return maxEntrySize; } /** * Set the maximum cache size */ public void setMaxLargeCacheSize(long mCacheSize){ maxLargeFileCacheSize = mCacheSize; } /** * Get the maximum cache size */ public long getMaxLargeCacheSize(){ return maxLargeFileCacheSize; } /** * Set the maximum cache size */ public void setMaxSmallCacheSize(long mCacheSize){ maxSmallFileCacheSize = mCacheSize; } /** * Get the maximum cache size */ public long getMaxSmallCacheSize(){ return maxSmallFileCacheSize; } /** * Is the fileCache enabled. */ public boolean isFileCacheEnabled(){ return isFileCacheEnabled; } /** * Is the file caching mechanism enabled. */ public void setFileCacheIsEnabled(boolean isFileCacheEnabled){ this.isFileCacheEnabled = isFileCacheEnabled; } /** * Is the large file cache support enabled. */ public void setLargeFileCacheEnabled(boolean isLargeEnabled){ this.isLargeFileCacheEnabled = isLargeEnabled; } /** * Is the large file cache support enabled. */ public boolean getLargeFileCacheEnabled(){ return isLargeFileCacheEnabled; } // ---------------------------- Async-------------------------------// /** * Enable the {@link AsyncHandler} used when asynchronous */ public void setEnableAsyncExecution(boolean asyncExecution){ this.asyncExecution = asyncExecution; if (running){ reconfigureAsyncExecution(); } } /** * Return true when asynchronous execution is * enabled. */ public boolean getEnableAsyncExecution(){ return asyncExecution; } /** * Set the {@link AsyncHandler} used when asynchronous execution is * enabled. */ public void setAsyncHandler(AsyncHandler asyncHandler){ this.asyncHandler = asyncHandler; } /** * Return the {@link AsyncHandler} used when asynchronous execution is * enabled. */ public AsyncHandler getAsyncHandler(){ return asyncHandler; } // ------------------------------------------------------------------- // /** * Set the logger used by this instance. */ public static void setLogger(Logger l){ if ( l != null ) logger = l; } /** * Return the logger used by the Grizzly classes. */ public static Logger logger(){ return logger; } /** * Set the document root folder */ public static void setWebAppRootPath(String rf){ rootFolder = rf; } /** * Return the folder's root where application are deployed. */ public static String getWebAppRootPath(){ return rootFolder; } // ------------------------------------------------------ Debug ---------// /** * Display the Grizzly configuration parameters. */ private void displayConfiguration(){ if (displayConfiguration){ logger.log(Level.INFO, "\n Grizzly configuration for port " + port + "\n\t maxThreads: " + maxThreads + "\n\t minThreads: " + minWorkerThreads + "\n\t ByteBuffer size: " + requestBufferSize + "\n\t maxHttpHeaderSize: " + maxHttpHeaderSize + "\n\t maxKeepAliveRequests: " + maxKeepAliveRequests + "\n\t keepAliveTimeoutInSeconds: " + keepAliveTimeoutInSeconds + "\n\t Static File Cache enabled: " + isFileCacheEnabled + "\n\t Static resources directory: " + new File(rootFolder).getAbsolutePath() + "\n\t Adapter : " + (adapter == null ? null : adapter.getClass().getName() ) + "\n\t Thread Pool (Pipeline): " + pipelineClassName + "\n\t Asynchronous Request Processing enabled: " + asyncExecution); } } /** * Return true if the reponse is buffered. */ public boolean getBufferResponse() { return bufferResponse; } /** * trueif the reponse willk be buffered. */ public void setBufferResponse(boolean bufferResponse) { this.bufferResponse = bufferResponse; } /** * Enable Application Resource Allocation Grizzly Extension. */ public void enableRcmSupport(boolean rcmSupport){ this.rcmSupport = rcmSupport; } /** * Returns whether * Application Resource Allocation Grizzly Extension is supported * @return is RCM supported */ public boolean isRcmSupported() { return rcmSupport; } // ----------------------------------------------Setter/Getter-----------// public KeepAliveCountManager getKeepAliveCounter() { return keepAliveCounter; } // ------------------------------------------------------ Compression ---// protected void configureCompression(DefaultProcessorTask processorTask){ processorTask.addNoCompressionUserAgent(noCompressionUserAgents); parseComressableMimeTypes(); processorTask.setCompressableMimeTypes(parsedCompressableMimeTypes); processorTask.setCompressionMinSize(compressionMinSize); processorTask.setCompression(compression); processorTask.addRestrictedUserAgent(restrictedUserAgents); } public String getCompression() { return compression; } public void setCompression(String compression) { this.compression = compression; } public String getNoCompressionUserAgents() { return noCompressionUserAgents; } public void setNoCompressionUserAgents(String noCompressionUserAgents) { this.noCompressionUserAgents = noCompressionUserAgents; } public String getRestrictedUserAgents() { return restrictedUserAgents; } public void setRestrictedUserAgents(String restrictedUserAgents) { this.restrictedUserAgents = restrictedUserAgents; } public String getCompressableMimeTypes() { return compressableMimeTypes; } public void setCompressableMimeTypes(String compressableMimeTypes) { this.compressableMimeTypes = compressableMimeTypes; } private void parseComressableMimeTypes() { if (compressableMimeTypes == null) { parsedCompressableMimeTypes = new String[0]; return; } int hash = -1; if ((hash = compressableMimeTypes.hashCode()) == parsedComressableMimeTypesHash) return; List compressableMimeTypeList = new ArrayList (4); StringTokenizer st = new StringTokenizer(compressableMimeTypes, ","); while(st.hasMoreTokens()) { compressableMimeTypeList.add(st.nextToken().trim()); } String[] tmpParsedCompressableMimeTypes = new String[compressableMimeTypeList.size()]; compressableMimeTypeList.toArray(tmpParsedCompressableMimeTypes); parsedCompressableMimeTypes = tmpParsedCompressableMimeTypes; parsedComressableMimeTypesHash = hash; } public int getCompressionMinSize() { return compressionMinSize; } public void setCompressionMinSize(int compressionMinSize) { this.compressionMinSize = compressionMinSize; } // ------------------------------------------------------------------- // public int getSelectorReadThreadsCount() { return readThreadsCount; } public void setSelectorReadThreadsCount(int readThreadsCount) { this.readThreadsCount = readThreadsCount; } public Pipeline getProcessorPipeline() { return processorPipeline; } public String getPipelineClassName() { return pipelineClassName; } public void setPipelineClassName(String pipelineClassName) { this.pipelineClassName = pipelineClassName; } public int getMinWorkerThreads() { return minWorkerThreads; } public void setMinWorkerThreads(int minWorkerThreads) { this.minWorkerThreads = minWorkerThreads; } public int getThreadsIncrement() { return threadsIncrement; } public void setThreadsIncrement(int threadsIncrement) { this.threadsIncrement = threadsIncrement; } public boolean isUseDirectByteBuffer() { return useDirectByteBuffer; } public void setUseDirectByteBuffer(boolean useDirectByteBuffer) { this.useDirectByteBuffer = useDirectByteBuffer; } public void setDisplayConfiguration(boolean displayConfiguration) { this.displayConfiguration = displayConfiguration; } public boolean isUseByteBufferView() { return useByteBufferView; } public void setUseByteBufferView(boolean useByteBufferView) { this.useByteBufferView = useByteBufferView; } public static int getSelectorTimeout() { return selectorTimeout; } public static void setSelectorTimeout(int aSelectorTimeout) { selectorTimeout = aSelectorTimeout; } public int getMaxQueueSizeInBytes() { return maxQueueSizeInBytes; } public void setMaxQueueSizeInBytes(int maxQueueSizeInBytes) { this.maxQueueSizeInBytes = maxQueueSizeInBytes; } public String getAlgorithmClassName() { return algorithmClassName; } public void setAlgorithmClassName(String algorithmClassName) { this.algorithmClassName = algorithmClassName; } public int getSsBackLog() { return ssBackLog; } public void setSsBackLog(int ssBackLog) { this.ssBackLog = ssBackLog; } public String getDefaultResponseType() { return defaultResponseType; } public void setDefaultResponseType(String defaultResponseType) { this.defaultResponseType = defaultResponseType; } public String getForcedRequestType() { return forcedRequestType; } public void setForcedRequestType(String forcedRequestType) { this.forcedRequestType = forcedRequestType; } public ConcurrentLinkedQueue getActiveProcessorTasks() { return activeProcessorTasks; } public ClassLoader getClassLoader() { return classLoader; } /** * Set the ClassLoader
used to load configurable * classes (Pipeline, StreamAlgorithm). */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public boolean isEnableNioLogging() { return enableNioLogging; } public void setEnableNioLogging(boolean enableNioLogging) { this.enableNioLogging = enableNioLogging; } public int getMaxPostSize() { return maxPostSize; } public void setMaxPostSize(int maxPostSize) { this.maxPostSize = maxPostSize; } public void setReuseAddress(boolean reuseAddress){ this.reuseAddress = reuseAddress; } public boolean getReuseAddress(){ return reuseAddress; } public SelectorThreadKeyHandler getSelectorThreadKeyHandler(){ return keyHandler; } /** * Set the flag to control upload time-outs. */ public void setDisableUploadTimeout(boolean isDisabled) { disableUploadTimeout = isDisabled; } /** * Get the flag that controls upload time-outs. */ public boolean getDisableUploadTimeout() { return disableUploadTimeout; } /** * Set the upload timeout. */ public void setUploadTimeout(int uploadTimeout) { this.uploadTimeout = uploadTimeout ; } /** * Get the upload timeout. */ public int getUploadTimeout() { return uploadTimeout; } // --------------------------------------------------------------------- // /** * Return a configured property. */ public Object getProperty(String name) { return properties.get(name); } /** * Set a configured property. */ public void setProperty(String name, Object value) { properties.put(name, value); } /** * remove a configured property. */ public void removeProperty(String name) { properties.remove(name); } }