dev@grizzly.java.net

Re: About recycling the server channel in XXXSelectorHandler

From: Jeanfrancois Arcand <Jeanfrancois.Arcand_at_Sun.COM>
Date: Thu, 18 Jun 2009 11:08:41 -0400

Salut,

Bongjae Chang wrote:
> Hi,
>
> I created new SelectorHandler like ReusableTCP|UDPSelectorHandler and SelectorHanderFactory#createSelectorHandler( Protocol, boolean reusable ).
>
> And TCP|UDPSelectorHandler was modified a little bit. Of course, original logic of TCP|UDPSelectorHandler was preserved.
>
> Could you review this again?

see inline...


>
> Thanks.
> --
> Bongjae Chang
>
> Index: test/java/com/sun/grizzly/ReusableSelectorHandlerTest.java
> ===================================================================
> --- test/java/com/sun/grizzly/ReusableSelectorHandlerTest.java (revision 0)
> +++ test/java/com/sun/grizzly/ReusableSelectorHandlerTest.java (revision 0)
> @@ -0,0 +1,181 @@
> +/*
> + *
> + * 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;
> +
> +import junit.framework.TestCase;
> +
> +import java.net.InetAddress;
> +import java.net.InetSocketAddress;
> +import java.net.UnknownHostException;
> +import java.net.Socket;
> +import java.net.BindException;
> +import java.net.SocketAddress;
> +import java.io.IOException;
> +import java.util.logging.Level;
> +
> +import com.sun.grizzly.utils.ControllerUtils;
> +
> +/**
> + * @author Bongjae Chang
> + * @date 2009. 6. 15
> + */
> +public class ReusableSelectorHandlerTest extends TestCase {
> +
> + private static final int PORT = 17521;
> + private static final int SLEEP_TIME = 3000; // ms
> +
> + private final InetAddress localInetAddress;
> + private final InetSocketAddress localInetSocketAddress;
> +
> + public ReusableSelectorHandlerTest() throws UnknownHostException {
> + localInetAddress = InetAddress.getLocalHost();
> + localInetSocketAddress = new InetSocketAddress( localInetAddress, PORT );
> + }
> +
> + public void testSimpleTCPConnect() throws IOException {
> + final Controller controller = new Controller();
> + SelectorHandler selectorHandler = SelectorHandlerFactory.createSelectorHandler( Controller.Protocol.TCP, true );
> + ( (TCPSelectorHandler)selectorHandler ).setPort( PORT );
> + ( (TCPSelectorHandler)selectorHandler ).setInet( localInetAddress );
> + ( (TCPSelectorHandler)selectorHandler ).setReuseAddress( false );
> + controller.addSelectorHandler( selectorHandler );
> +
> + Socket clientSocket = null;
> + try {
> + ControllerUtils.startController( controller );
> +
> + boolean result = false;
> + Controller.logger().log( Level.INFO, "Try to get a connector handler with the local address which already has been bound." );
> + try {
> + tryToConnect( controller, Controller.Protocol.TCP, null, localInetSocketAddress );
> + } catch( IOException ie ) {
> + if( ie instanceof BindException ) {
> + result = true;
> + Controller.logger().log( Level.INFO, "Got the expected BindException." );
> + assertTrue( "Got the expected BindException.", true );
> + } else {
> + Controller.logger().log( Level.INFO, "Got the unexpected error.", ie );
> + assertTrue( "Got the unexpected error.", false );
> + }
> + }
> + if( !result )
> + assertTrue( "The BindException was expected.", false );
> +
> + Controller.logger().log( Level.INFO, "Try to connect the local server." );
> + clientSocket = new Socket( localInetAddress, PORT );
> + Controller.logger().log( Level.INFO, "Wait for " + SLEEP_TIME + "(ms)" );
> + try {
> + Thread.sleep( SLEEP_TIME );
> + } catch( InterruptedException e ) {
> + }
> +
> + Controller.logger().log( Level.INFO, "Try to get a connector handler with the local address which already has been bound again." );
> + try {
> + tryToConnect( controller, Controller.Protocol.TCP, clientSocket.getLocalSocketAddress(), localInetSocketAddress );
> + } catch( IOException ie ) {
> + Controller.logger().log( Level.INFO, "Got the unexpected error.", ie );
> + assertTrue( "Got the unexpected error.", false );
> + throw ie;
> + }
> + } finally {
> + if( clientSocket != null ) {
> + try {
> + clientSocket.shutdownInput();
> + } catch( IOException e ) {
> + }
> + try {
> + clientSocket.shutdownOutput();
> + } catch( IOException e ) {
> + }
> + try {
> + clientSocket.close();
> + } catch( IOException e ) {
> + }
> + }
> + controller.stop();
> + }
> + }
> +
> + public void testSimpleUDPConnect() throws IOException {
> + final Controller controller = new Controller();
> + SelectorHandler selectorHandler = SelectorHandlerFactory.createSelectorHandler( Controller.Protocol.UDP, true );
> + ( (UDPSelectorHandler)selectorHandler ).setPort( PORT );
> + ( (UDPSelectorHandler)selectorHandler ).setInet( localInetAddress );
> + ( (UDPSelectorHandler)selectorHandler ).setReuseAddress( false );
> + controller.addSelectorHandler( selectorHandler );
> +
> + try {
> + ControllerUtils.startController( controller );
> +
> + Controller.logger().log( Level.INFO, "Try to get a connector handler with the local address which already has been bound." );
> + try {
> + tryToConnect( controller, Controller.Protocol.UDP, localInetSocketAddress, localInetSocketAddress );
> + } catch( IOException ie ) {
> + Controller.logger().log( Level.INFO, "Got the unexpected error.", ie );
> + assertTrue( "Got the unexpected error.", false );
> + throw ie;
> + }
> + } finally {
> + controller.stop();
> + }
> + }
> +
> + private void tryToConnect( Controller controller, Controller.Protocol protocol, SocketAddress remote, SocketAddress local ) throws IOException {
> + ConnectorHandler connectorHandler = null;
> + try {
> + connectorHandler = controller.acquireConnectorHandler( protocol );
> + connectorHandler.connect( remote, local );
> + } finally {
> + if( connectorHandler != null ) {
> + try {
> + connectorHandler.close();
> + } catch( IOException e ) {
> + e.printStackTrace();
> + }
> + controller.releaseConnectorHandler( connectorHandler );
> + }
> + }
> + }
> +
> + public static void main( String[] args ) throws IOException {
> + ReusableSelectorHandlerTest test = new ReusableSelectorHandlerTest();
> + test.testSimpleTCPConnect();
> + test.testSimpleUDPConnect();
> + }
> +}
> Index: main/java/com/sun/grizzly/SelectorHandlerFactory.java
> ===================================================================
> --- main/java/com/sun/grizzly/SelectorHandlerFactory.java (revision 0)
> +++ main/java/com/sun/grizzly/SelectorHandlerFactory.java (revision 0)
> @@ -0,0 +1,70 @@
> +/*
> + *
> + * 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;
> +
> +/**
> + * @author Bongjae Chang
> + * @date 2009. 6. 18

[minor] Remove Date, add small documentation


> + */
> +public class SelectorHandlerFactory {
> +
> + public static SelectorHandler createSelectorHandler( Controller.Protocol protocol, boolean resuable ) {
> + SelectorHandler selectorHandler = null;
> + if( Controller.Protocol.TCP == protocol ) {
> + if( resuable )
> + selectorHandler = new ReusableTCPSelectorHandler();
> + else
> + selectorHandler = new TCPSelectorHandler();
> + } else if ( Controller.Protocol.UDP == protocol ) {
> + if( resuable )
> + selectorHandler = new ReusableUDPSelectorHandler();
> + else
> + selectorHandler = new UDPSelectorHandler();
> + } else if( Controller.Protocol.TLS == protocol ) {
> + if( resuable )
> + selectorHandler = new SSLSelectorHandler(); // not supported yet
> + else
> + selectorHandler = new SSLSelectorHandler();
> + } else {
> + // default
> + selectorHandler = new TCPSelectorHandler();
> + }
> + return selectorHandler;
> + }
> +}
> Index: main/java/com/sun/grizzly/ReusableTCPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/ReusableTCPSelectorHandler.java (revision 0)
> +++ main/java/com/sun/grizzly/ReusableTCPSelectorHandler.java (revision 0)
> @@ -0,0 +1,140 @@
> +/*
> + *
> + * 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;
> +
> +import java.net.SocketAddress;
> +import java.net.InetSocketAddress;
> +import java.net.Socket;
> +import java.nio.channels.SocketChannel;
> +import java.nio.channels.SelectableChannel;
> +import java.nio.channels.SelectionKey;
> +import java.nio.channels.ServerSocketChannel;
> +import java.util.concurrent.ConcurrentHashMap;
> +import java.util.concurrent.CopyOnWriteArrayList;
> +import java.io.IOException;
> +
> +/**
> + * @author Bongjae Chang
> + * @date 2009. 6. 18

[minor] Remove Date, add small documentation. Explain what it does :-)


> + */
> +public class ReusableTCPSelectorHandler extends TCPSelectorHandler {
> +
> + private final ConcurrentHashMap<SocketAddress, SocketChannel> acceptedSocketChannelMap =
> + new ConcurrentHashMap<SocketAddress, SocketChannel>();
> + private final CopyOnWriteArrayList<SocketChannel> reusableSocketChannels =
> + new CopyOnWriteArrayList<SocketChannel>();
> +
> + public ReusableTCPSelectorHandler() {
> + super( Role.CLIENT_SERVER );
> + }
> +
> + @Override
> + protected SelectableChannel getSelectableChannel( SocketAddress remoteAddress, SocketAddress localAddress ) throws IOException {
> + SelectableChannel selectableChannel = null;
> + if( localAddress != null ) {
> + if( inet != null && localAddress instanceof InetSocketAddress ) {
> + InetSocketAddress inetSocketAddress = (InetSocketAddress)localAddress;
> + if( inet.equals( inetSocketAddress.getAddress() ) )
> + selectableChannel = getUsedSelectableChannel( remoteAddress );
> + }
> + } else {
> + selectableChannel = getUsedSelectableChannel( remoteAddress );
> + }
> + if( selectableChannel == null )
> + selectableChannel = super.getSelectableChannel( remoteAddress, localAddress );
> + return selectableChannel;
> + }
> +
> + private SelectableChannel getUsedSelectableChannel( SocketAddress remoteAddress ) {
> + if( remoteAddress != null ) {
> + SocketChannel acceptedSocketChannel = acceptedSocketChannelMap.get( remoteAddress );
> + if( acceptedSocketChannel != null )
> + reusableSocketChannels.add( acceptedSocketChannel );
> + return acceptedSocketChannel;
> + } else {
> + return null;
> + }
> + }
> +
> + /**
> + * {_at_inheritDoc}
> + */
> + @Override
> + public void shutdown() {
> + super.shutdown();
> + acceptedSocketChannelMap.clear();
> + reusableSocketChannels.clear();
> + }
> +
> + /**
> + * {_at_inheritDoc}
> + */
> + @Override
> + public SelectableChannel acceptWithoutRegistration( SelectionKey key ) throws IOException {
> + SocketChannel acceptedSocketChannel = ( (ServerSocketChannel)key.channel() ).accept();
> + if( acceptedSocketChannel != null ) {
> + SocketAddress remoteSocketAddress = null;
> + Socket acceptedSocket = acceptedSocketChannel.socket();
> + if( acceptedSocket != null )
> + remoteSocketAddress = acceptedSocket.getRemoteSocketAddress();
> + if( remoteSocketAddress != null )
> + acceptedSocketChannelMap.put( remoteSocketAddress, acceptedSocketChannel );
> + }
> + return acceptedSocketChannel;
> + }
> +
> + /**
> + * {_at_inheritDoc}
> + */
> + @Override
> + public void closeChannel( SelectableChannel channel ) {
> + if( channel instanceof SocketChannel ) {
> + SocketChannel socketChannel = (SocketChannel)channel;
> + if( reusableSocketChannels.remove( socketChannel ) )
> + return;
> + Socket socket = socketChannel.socket();
> + SocketAddress remoteSocketAddress = null;
> + if( socket != null )
> + remoteSocketAddress = socket.getRemoteSocketAddress();
> + if( remoteSocketAddress != null )
> + acceptedSocketChannelMap.remove( remoteSocketAddress );
> + }
> + super.closeChannel( channel );
> + }
> +}
> Index: main/java/com/sun/grizzly/UDPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/UDPSelectorHandler.java (revision 3351)
> +++ main/java/com/sun/grizzly/UDPSelectorHandler.java (working copy)
> @@ -38,11 +38,9 @@
>
> package com.sun.grizzly;
>
> -import com.sun.grizzly.SelectionKeyOP.ConnectSelectionKeyOP;
> import com.sun.grizzly.async.UDPAsyncQueueReader;
> import com.sun.grizzly.async.UDPAsyncQueueWriter;
> import com.sun.grizzly.util.Copyable;
> -import com.sun.grizzly.util.State;
> import java.io.IOException;
> import java.net.BindException;
> import java.net.DatagramSocket;
> @@ -138,9 +136,9 @@
> ConcurrentQueueDelegateCIH(
> getConnectorInstanceHandlerDelegate());
>
> - datagramChannel = DatagramChannel.open();
> selector = Selector.open();
> if (role != Role.CLIENT){
> + datagramChannel = DatagramChannel.open();
> datagramSocket = datagramChannel.socket();
> datagramSocket.setReuseAddress(reuseAddress);
> if (inet == null)
> @@ -159,35 +157,14 @@
> }
> }
>
> -
> - /**
> - * Register a CallBackHandler to this Selector.
> - *
> - * @param remoteAddress remote address to connect
> - * @param localAddress local address to bin
> - * @param callbackHandler {_at_link CallbackHandler}
> - * @throws java.io.IOException
> - */
> @Override
> - protected void connect(SocketAddress remoteAddress, SocketAddress localAddress,
> - CallbackHandler callbackHandler) throws IOException {

Do we need to remove the above?


> -
> + protected SelectableChannel getSelectableChannel( SocketAddress remoteAddress, SocketAddress localAddress ) throws IOException {
> DatagramChannel newDatagramChannel = DatagramChannel.open();
> - newDatagramChannel.socket().setReuseAddress(reuseAddress);
> - if (localAddress != null) {
> - newDatagramChannel.socket().bind(localAddress);
> - }
> -
> - newDatagramChannel.configureBlocking(false);
> -
> - SelectionKeyOP.ConnectSelectionKeyOP keyOP = new ConnectSelectionKeyOP();
> -
> - keyOP.setOp(SelectionKey.OP_CONNECT);
> - keyOP.setChannel(newDatagramChannel);
> - keyOP.setRemoteAddress(remoteAddress);
> - keyOP.setCallbackHandler(callbackHandler);
> - opToRegister.offer(keyOP);
> - selector.wakeup();
> + newDatagramChannel.socket().setReuseAddress( reuseAddress );
> + if( localAddress != null )
> + newDatagramChannel.socket().bind( localAddress );
> + newDatagramChannel.configureBlocking( false );
> + return newDatagramChannel;
> }
>
> /**
> @@ -196,19 +173,19 @@
> @Override
> protected void onConnectOp(Context ctx,
> SelectionKeyOP.ConnectSelectionKeyOP selectionKeyOp) throws IOException {
> - DatagramChannel newDatagramChannel = (DatagramChannel) selectionKeyOp.getChannel();
> + DatagramChannel datagramChannel = (DatagramChannel) selectionKeyOp.getChannel();
> SocketAddress remoteAddress = selectionKeyOp.getRemoteAddress();
> CallbackHandler callbackHandler = selectionKeyOp.getCallbackHandler();
>
> CallbackHandlerSelectionKeyAttachment attachment =
> new CallbackHandlerSelectionKeyAttachment(callbackHandler);
>
> - SelectionKey key = newDatagramChannel.register(selector,
> + SelectionKey key = datagramChannel.register(selector,
> SelectionKey.OP_READ | SelectionKey.OP_WRITE, attachment);
> attachment.associateKey(key);
>
> try {
> - newDatagramChannel.connect(remoteAddress);
> + datagramChannel.connect(remoteAddress);
> } catch(Exception e) {
> if (logger.isLoggable(Level.FINE)) {
> logger.log(Level.FINE, "Exception occured when tried to connect datagram channel", e);
> @@ -224,44 +201,26 @@
> */
> @Override
> public void shutdown(){
> - // If shutdown was called for this SelectorHandler
> - if (isShutDown.getAndSet(true)) return;
> -
> - stateHolder.setState(State.STOPPED);
> -
> + super.shutdown();
> try {
> - if ( datagramSocket != null )
> + if ( datagramSocket != null ) {
> datagramSocket.close();
> + datagramSocket = null;
> + }
> } catch (Throwable ex){
> Controller.logger().log(Level.SEVERE,
> "closeSocketException",ex);
> }
>
> try{
> - if ( datagramChannel != null)
> + if ( datagramChannel != null) {
> datagramChannel.close();
> + datagramChannel = null;
> + }
> } catch (Throwable ex){
> Controller.logger().log(Level.SEVERE,
> "closeSocketException",ex);
> }
> -
> - try{
> - if ( selector != null)
> - selector.close();
> - } catch (Throwable ex){
> - Controller.logger().log(Level.SEVERE,
> - "closeSocketException",ex);
> - }
> -
> - if (asyncQueueReader != null) {
> - asyncQueueReader.close();
> - asyncQueueReader = null;
> - }
> -
> - if (asyncQueueWriter != null) {
> - asyncQueueWriter.close();
> - asyncQueueWriter = null;
> - }
> }
>
>
> Index: main/java/com/sun/grizzly/ReusableUDPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/ReusableUDPSelectorHandler.java (revision 0)
> +++ main/java/com/sun/grizzly/ReusableUDPSelectorHandler.java (revision 0)
> @@ -0,0 +1,89 @@
> +/*
> + *
> + * 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;
> +
> +import java.nio.channels.SelectableChannel;
> +import java.io.IOException;
> +import java.net.SocketAddress;
> +import java.net.InetSocketAddress;
> +
> +/**
> + * @author Bongjae Chang
> + * @date 2009. 6. 18
> + */

[minor] Remove Date, add small documentation.

> +public class ReusableUDPSelectorHandler extends UDPSelectorHandler {
> +
> + public ReusableUDPSelectorHandler() {
> + super( Role.CLIENT_SERVER );
> + }
> +
> + @Override
> + protected SelectableChannel getSelectableChannel( SocketAddress remoteAddress, SocketAddress localAddress ) throws IOException {
> + SelectableChannel selectableChannel = null;
> + if( localAddress != null ) {
> + if( inet != null && localAddress instanceof InetSocketAddress ) {
> + InetSocketAddress inetSocketAddress = (InetSocketAddress)localAddress;
> + if( inet.equals( inetSocketAddress.getAddress() ) )
> + selectableChannel = getUsedSelectableChannel();
> + }
> + } else {
> + selectableChannel = getUsedSelectableChannel();
> + }
> + if( selectableChannel == null )
> + selectableChannel = super.getSelectableChannel( remoteAddress, localAddress );
> + return selectableChannel;
> + }
> +
> + private SelectableChannel getUsedSelectableChannel() {
> + if( role != Role.CLIENT && datagramChannel != null && datagramSocket != null )
> + return datagramChannel;
> + else
> + return null;
> + }
> +
> + /**
> + * {_at_inheritDoc}
> + */
> + @Override
> + public void closeChannel( SelectableChannel channel ) {
> + if( datagramChannel == channel )
> + return;
> + super.closeChannel( channel );
> + }
> +}
> Index: main/java/com/sun/grizzly/TCPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/TCPSelectorHandler.java (revision 3351)
> +++ main/java/com/sun/grizzly/TCPSelectorHandler.java (working copy)
> @@ -274,7 +274,7 @@
>
> private long lastSpinTimestamp;
> private int emptySpinCounter;
> -
> +
> public TCPSelectorHandler(){
> this(Role.CLIENT_SERVER);
> }
> @@ -397,15 +397,13 @@
>
> serverSocketChannel.configureBlocking(false);
> serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
> +
> + serverSocket.setSoTimeout(serverTimeout);
> }
> ctx.getController().notifyReady();
> } catch (SocketException ex){
> throw new BindException(ex.getMessage() + ": " + port + "=" + this);
> }
> -
> - if (role != Role.CLIENT){
> - serverSocket.setSoTimeout(serverTimeout);
> - }
> }
>
> /**
> @@ -531,7 +529,7 @@
> }else{
> ((Runnable)obj).run();
> }
> - }catch(Throwable t){
> + }catch(Throwable t){
> logger.log(Level.FINEST, "doExecutePendiongIO failed.", t);
> }
> }
> @@ -567,7 +565,7 @@
> * Register a SelectionKey to this Selector.<br>
> * Storing each interest type in different queues removes the need of wrapper (SelectionKeyOP)
> * while lowering thread contention due to the load is spread out on different queues.
> - *
> + *
> * @param key
> * @param ops
> */
> @@ -595,7 +593,7 @@
> opToRegister.offer(new SelectionKeyOP(null,ops,channel));
> wakeUp();
> }
> -
> +
> /**
> * Workaround for NIO issue 6524172
> */
> @@ -617,25 +615,25 @@
> */
> protected void connect(SocketAddress remoteAddress, SocketAddress localAddress,
> CallbackHandler callbackHandler) throws IOException {
> -
> - SocketChannel socketChannel = SocketChannel.open();
> - socketChannel.socket().setReuseAddress(reuseAddress);
> - if (localAddress != null) {
> - socketChannel.socket().bind(localAddress);
> - }
> -
> - socketChannel.configureBlocking(false);
> -
> + SelectableChannel selectableChannel = getSelectableChannel( remoteAddress, localAddress );
> SelectionKeyOP.ConnectSelectionKeyOP keyOP = new ConnectSelectionKeyOP();
> -
> keyOP.setOp(SelectionKey.OP_CONNECT);
> - keyOP.setChannel(socketChannel);
> + keyOP.setChannel(selectableChannel);
> keyOP.setRemoteAddress(remoteAddress);
> keyOP.setCallbackHandler(callbackHandler);
> opToRegister.offer(keyOP);
> wakeUp();
> }
>
> + protected SelectableChannel getSelectableChannel( SocketAddress remoteAddress, SocketAddress localAddress ) throws IOException {
> + SocketChannel newSocketChannel = SocketChannel.open();
> + newSocketChannel.socket().setReuseAddress( reuseAddress );
> + if( localAddress != null )
> + newSocketChannel.socket().bind( localAddress );
> + newSocketChannel.configureBlocking( false );
> + return newSocketChannel;
> + }
> +
> /**
> * {_at_inheritDoc}
> */
> @@ -691,16 +689,20 @@
> }
>
> try{
> - if (serverSocket != null)
> + if (serverSocket != null) {
> serverSocket.close();
> + serverSocket = null;
> + }
> } catch (Throwable ex){
> Controller.logger().log(Level.SEVERE,
> "serverSocket.close",ex);
> }
>
> try{
> - if (serverSocketChannel != null)
> + if (serverSocketChannel != null) {
> serverSocketChannel.close();
> + serverSocketChannel = null;
> + }
> } catch (Throwable ex){
> Controller.logger().log(Level.SEVERE,
> "serverSocketChannel.close",ex);
> @@ -845,7 +847,7 @@
> // Added because of incompatibility with Grizzly 1.6.0
> context.setSelectorHandler(this);
>
> - CallbackHandlerContextTask task =
> + CallbackHandlerContextTask task =
> context.getCallbackHandlerContextTask(callbackHandler);
> boolean isRunInSeparateThread = true;
>
> @@ -1268,14 +1270,14 @@
> * @return {_at_link Context}
> */
> protected NIOContext pollContext(final Context serverContext,
> - final SelectionKey key, final Context.OpType opType) {
> + final SelectionKey key, final Context.OpType opType) {
> Controller c = serverContext.getController();
> ProtocolChain protocolChain = instanceHandler != null ?
> instanceHandler.poll() :
> c.getProtocolChainInstanceHandler().poll();
>
> final NIOContext context = (NIOContext)c.pollContext();
> - c.configureContext(key, opType, context, this);
> + c.configureContext(key, opType, context, this);
> context.setProtocolChain(protocolChain);
> return context;
> }
> @@ -1379,7 +1381,7 @@
> * {_at_inheritDoc}
> */
> public void resetSpinCounter(){
> - emptySpinCounter = 0;
> + emptySpinCounter = 0;
> }
>
> /**
>
>

Looks good.

Thanks!

-- Jeanfrancois