+1 on the great work!
Wonder if it might be worthy to revisit some of the discussion(s) we
about exception handling and logging in the past at one or more of
Project Grizzly meetings?
IIRC, Ken had some really good recommendations around exception handling
and logging.
I'll see if I can find some old meeting notes and post them here.
charlie ...
Jeanfrancois Arcand wrote:
> Hi Alexey,
>
> see inline. First, gigantic thanks for this great works!! Most of my
> comments relate to logging, the most important one is about certs and
> protocols.
>
> Thanks!
>
> -- Jeanfrancois
>
> Oleksiy Stashok wrote:
>> Hello,
>>
>> please review client SSL implementation[attached ssl.new] and code
>> change I propose additionally [attached ssl.diff].
>>
>> List of changes:
>> + add SSLCallbackHandler, where method onHandshake(IOEvent) added ->
>> generic <P extends CallbackHandler> was added to interface
>> ConnectorHandler
>> + SSLUtils changed, where possible pass SocketChannel instead of
>> SelectionKey. As there are situations, where SelectionKey is unknown.
>> May be we can do this change for other classes (where it makes sense).
>> + IOEvent.DefaultIOEvent implementation added to reduce the number of
>> anonym. inner classes.
>> + Removed TCP/UDP ConnectorInstanceHandler classes, as they have the
>> same implementation. Created
>> ConnectorInstanceHandler.ConcurrentQueueDelegateCIH class, which
>> implements the common logic and delegates creating of new
>> ConnectorHandlers to the provided Callable factory.
>>
>> + SSL client implementation added
>> + SSL client unit test
>>
>> WBR,
>> Alexey.
>>
>>
>>
>> # This patch file was generated by NetBeans IDE
>> # Following Index: paths are relative to:
>> C:\Projects\Grizzly\trunk\modules\grizzly\src
>> # This patch can be applied using context Tools: Patch action on
>> respective folder.
>> # It uses platform neutral UTF-8 encoding and \n newlines.
>> # Above lines and this line are ignored by the patching process.
>> Index: main/java/com/sun/grizzly/SSLCallbackHandler.java
>> ***
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLCallbackHandler.java
>> Locally New
>> ---
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLCallbackHandler.java
>> Locally New
>> ***************
>> *** 1,0 ****
>> --- 1,37 ----
>> + /*
>> + * The contents of this file are subject to the terms
>> + * of the Common Development and Distribution License
>> + * (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/CDDLv1.0.html or
>> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * See the License for the specific language governing
>> + * permissions and limitations under the License.
>> + *
>> + * When distributing Covered Code, include this CDDL
>> + * Header Notice in each file and include the License file
>> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * If applicable, add the following below the CDDL Header,
>> + * with the fields enclosed by brackets [] replaced by
>> + * you own identifying information:
>> + * "Portions Copyrighted [year] [name of copyright owner]"
>> + *
>> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
>> + */
>> + + package com.sun.grizzly;
>> + + /**
>> + * @author Alexey Stashok
>> + */
>> + public interface SSLCallbackHandler<E> extends CallbackHandler<E> {
>> + /**
>> + * This method is called when an non blocking OP_CONNECT is ready
>> + * to get processed.
>> + * @param ioEvent an object containing information about the
>> current + * non blocking connection. + */
>> + public void onHandshake(IOEvent<E> ioEvent);
>> + }
>> Index: main/java/com/sun/grizzly/SSLConfig.java
>> ***
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConfig.java
>> Locally New
>> ---
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConfig.java
>> Locally New
>> ***************
>> *** 1,0 ****
>> --- 1,183 ----
>> + /*
>> + * The contents of this file are subject to the terms
>> + * of the Common Development and Distribution License
>> + * (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/CDDLv1.0.html or
>> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * See the License for the specific language governing
>> + * permissions and limitations under the License.
>> + *
>> + * When distributing Covered Code, include this CDDL
>> + * Header Notice in each file and include the License file
>> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * If applicable, add the following below the CDDL Header,
>> + * with the fields enclosed by brackets [] replaced by
>> + * you own identifying information:
>> + * "Portions Copyrighted [year] [name of copyright owner]"
>> + *
>> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
>> + */
>> + + package com.sun.grizzly;
>> + + import java.io.FileInputStream;
>> + import java.security.KeyStore;
>> + import javax.net.ssl.KeyManagerFactory;
>> + import javax.net.ssl.SSLContext;
>> + import javax.net.ssl.TrustManagerFactory;
>> + + /**
>> + * @author Alexey Stashok
>> + */
>> + public class SSLConfig {
>> + public static SSLConfig DEFAULT_CONFIG = new SSLConfig();
>> + + private String trustStoreType = "JKS";
>> + private String keyStoreType = "JKS";
>> + + private char[] trustStorePass = "changeit".toCharArray();
>> + private char[] keyStorePass = "changeit".toCharArray();
>> + + private String trustStoreFile =
>> System.getProperty("javax.net.ssl.trustStore");
>> + private String keyStoreFile =
>> System.getProperty("javax.net.ssl.keyStore");
>> + + private String trustStoreAlgorithm = "SunX509";
>> + private String keyStoreAlgorithm = "SunX509";
>> + + private String securityProtocol = "TLS";
>> + + private boolean needClientAuth = false;
>> + + private boolean wantClientAuth = false;
>> + + public String getTrustStoreType() {
>> + return trustStoreType;
>> + }
>> + + public void setTrustStoreType(String trustStoreType) {
>> + this.trustStoreType = trustStoreType;
>> + }
>> + + public String getKeyStoreType() {
>> + return keyStoreType;
>> + }
>> + + public void setKeyStoreType(String keyStoreType) {
>> + this.keyStoreType = keyStoreType;
>> + }
>> + + public String getTrustStorePass() {
>> + return new String(trustStorePass);
>> + }
>> + + public void setTrustStorePass(String trustStorePass) {
>> + this.trustStorePass = trustStorePass.toCharArray();
>> + }
>> + + public String getKeyStorePass() {
>> + return new String(keyStorePass);
>> + }
>> + + public void setKeyStorePass(String keyStorePass) {
>> + this.keyStorePass = keyStorePass.toCharArray();
>> + }
>> + + public String getTrustStoreFile() {
>> + return trustStoreFile;
>> + }
>> + + public void setTrustStoreFile(String trustStoreFile) {
>> + this.trustStoreFile = trustStoreFile;
>> + }
>> + + public String getKeyStoreFile() {
>> + return keyStoreFile;
>> + }
>> + + public void setKeyStoreFile(String keyStoreFile) {
>> + this.keyStoreFile = keyStoreFile;
>> + }
>> + + public String getTrustStoreAlgorithm() {
>> + return trustStoreAlgorithm;
>> + }
>> + + public void setTrustStoreAlgorithm(String trustStoreAlgorithm) {
>> + this.trustStoreAlgorithm = trustStoreAlgorithm;
>> + }
>> + + public String getKeyStoreAlgorithm() {
>> + return keyStoreAlgorithm;
>> + }
>> + + public void setKeyStoreAlgorithm(String keyStoreAlgorithm) {
>> + this.keyStoreAlgorithm = keyStoreAlgorithm;
>> + }
>> + + public String getSecurityProtocol() {
>> + return securityProtocol;
>> + }
>> + + public void setSecurityProtocol(String securityProtocol) {
>> + this.securityProtocol = securityProtocol;
>> + }
>> + + public boolean isNeedClientAuth() {
>> + return needClientAuth;
>> + }
>> + + public void setNeedClientAuth(boolean needClientAuth) {
>> + this.needClientAuth = needClientAuth;
>> + }
>> + + public boolean isWantClientAuth() {
>> + return wantClientAuth;
>> + }
>> + + public void setWantClientAuth(boolean wantClientAuth) {
>> + this.wantClientAuth = wantClientAuth;
>> + }
>> + + public SSLContext createSSLContext() {
>> + SSLContext sslContext = null;
>> + + try{
>> + TrustManagerFactory trustManagerFactory = null;
>> + KeyManagerFactory keyManagerFactory = null;
>> + + try {
>> + KeyStore trustStore =
>> KeyStore.getInstance(keyStoreType);
>> + trustStore.load(new FileInputStream(trustStoreFile),
>> + trustStorePass);
>> + + trustManagerFactory =
>> +
>> TrustManagerFactory.getInstance(trustStoreAlgorithm);
>> + trustManagerFactory.init(trustStore);
>> + } catch (Exception e) {
>> + }
>> +
>
> Should we log the exception?
>
>> + try {
>> + KeyStore keyStore =
>> KeyStore.getInstance(keyStoreType);
>> + keyStore.load(new FileInputStream(keyStoreFile),
>> + keyStorePass);
>> + + +
>> keyManagerFactory =
>> +
>> KeyManagerFactory.getInstance(keyStoreAlgorithm);
>> + keyManagerFactory.init(keyStore, keyStorePass);
>> + } catch (Exception e) {
>> + }
>> +
>
> Should we log the exception?
>
>> + sslContext = SSLContext.getInstance(securityProtocol);
>> + sslContext.init(keyManagerFactory != null ?
>> keyManagerFactory.getKeyManagers() : null,
>> + trustManagerFactory != null ?
>> trustManagerFactory.getTrustManagers() : null,
>> + null);
>> + } catch(Exception ex){
>> + }
>> +
>
> Should we log the exception :-)
>
>> + return sslContext;
>> + }
>> + }
>> Index: test/java/com/sun/grizzly/SSLConnectionTest.java
>> ***
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\SSLConnectionTest.java
>> Locally New
>> ---
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\SSLConnectionTest.java
>> Locally New
>> ***************
>> *** 1,0 ****
>> --- 1,315 ----
>> + /*
>> + * The contents of this file are subject to the terms
>> + * of the Common Development and Distribution License
>> + * (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/CDDLv1.0.html or
>> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * See the License for the specific language governing
>> + * permissions and limitations under the License.
>> + *
>> + * When distributing Covered Code, include this CDDL
>> + * Header Notice in each file and include the License file
>> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * If applicable, add the following below the CDDL Header,
>> + * with the fields enclosed by brackets [] replaced by
>> + * you own identifying information:
>> + * "Portions Copyrighted [year] [name of copyright owner]"
>> + *
>> + * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
>> + */
>> + + package com.sun.grizzly;
>> + + import com.sun.grizzly.filter.SSLReadFilter;
>> + import com.sun.grizzly.utils.ControllerUtils;
>> + import com.sun.grizzly.utils.SSLEchoFilter;
>> + import java.io.IOException;
>> + import java.net.InetSocketAddress;
>> + import java.net.URL;
>> + import java.nio.ByteBuffer;
>> + import java.nio.channels.SelectionKey;
>> + import java.util.Arrays;
>> + import java.util.Enumeration;
>> + import java.util.concurrent.CountDownLatch;
>> + import java.util.concurrent.TimeUnit;
>> + import javax.net.ssl.SSLContext;
>> + import junit.framework.TestCase;
>> + + /**
>> + * @author Alexey Stashok
>> + */
>> + public class SSLConnectionTest extends TestCase {
>> + + private static final String TRUST_STORE_PROP =
>> "javax.net.ssl.trustStore";
>> + private static final String KEY_STORE_PROP =
>> "javax.net.ssl.keyStore";
>> + + public static final int PORT = 18888;
>> + public static final int PACKETS_COUNT = 10;
>> + public static final int CLIENTS_COUNT = 99;
>> + + /**
>> + * A <code>SSLCallbackHandler</code> handler invoked by the
>> TCPSelectorHandler
>> + * when a non blocking operation is ready to be processed.
>> + */
>> + private SSLCallbackHandler callbackHandler;
>> + + private SSLConfig sslConfig;
>> + + + @Override
>> + public void setUp() {
>> + sslConfig = new SSLConfig();
>> + ClassLoader cl = getClass().getClassLoader();
>> + // override system properties
>> + URL cacertsUrl = cl.getResource("ssltest-cacerts.jks");
>> + if (cacertsUrl != null) {
>> + sslConfig.setTrustStoreFile(cacertsUrl.getFile());
>> + }
>> + + // override system properties
>> + URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
>> + if (keystoreUrl != null) {
>> + sslConfig.setKeyStoreFile(keystoreUrl.getFile());
>> + }
>> + + SSLConfig.DEFAULT_CONFIG = sslConfig;
>> + }
>> + + public void testSimplePacket() throws IOException {
>> + + final Controller controller =
>> createSSLController(SSLConfig.DEFAULT_CONFIG.createSSLContext());
>> + ControllerUtils.startController(controller);
>> + final SSLConnectorHandler sslConnector =
>> (SSLConnectorHandler) controller.
>> + acquireConnectorHandler(Controller.Protocol.TLS);
>> + + try {
>> + final byte[] testData = "Hello".getBytes();
>> + final byte[] response = new
>> byte[sslConnector.getApplicationBufferSize()];
>> + + final ByteBuffer writeBB = ByteBuffer.wrap(testData);
>> + final ByteBuffer readBB = ByteBuffer.wrap(response);
>> + final CountDownLatch handshakeDoneLatch = new
>> CountDownLatch(1);
>> + final CountDownLatch responseArrivedLatch = new
>> CountDownLatch(1);
>> + + callbackHandler = createCallbackHandler(controller,
>> sslConnector, responseArrivedLatch, handshakeDoneLatch, writeBB,
>> readBB);
>> + + try {
>> + sslConnector.connect(new
>> InetSocketAddress("localhost", PORT), callbackHandler);
>> + } catch (Throwable t) {
>> + t.printStackTrace();
>> + }
>> + + waitOnLatch(handshakeDoneLatch, 10, TimeUnit.SECONDS);
>> + assertTrue(sslConnector.isHandshakeDone());
>> + + long nWrite = sslConnector.write(writeBB, false);
>> + + long nRead = -1;
>> + + // All bytes written
>> + if (nWrite == testData.length) {
>> + nRead = sslConnector.read(readBB, false);
>> + }
>> + + if (nRead != nWrite) {
>> + waitOnLatch(responseArrivedLatch, 5,
>> TimeUnit.SECONDS);
>> + }
>> + + assertTrue(Arrays.equals(testData, toArray(readBB)));
>> + } finally {
>> + try {
>> + sslConnector.close();
>> + controller.releaseConnectorHandler(sslConnector);
>> + controller.stop();
>> + } catch (Throwable t) {
>> + t.printStackTrace();
>> + }
>> + }
>> + }
>> + + public void testSeveralPackets() throws IOException {
>> + final Controller controller =
>> createSSLController(SSLConfig.DEFAULT_CONFIG.createSSLContext());
>> + ControllerUtils.startController(controller);
>> + try {
>> + + for(int i=0; i<CLIENTS_COUNT; i++) {
>> + //System.out.println("Client#" + i);
>> + final SSLConnectorHandler sslConnector =
>> + (SSLConnectorHandler)
>> controller.acquireConnectorHandler(Controller.Protocol.TLS);
>> + sslConnector.setController(controller);
>> + final byte[] testData = new String("Hello. Client#"
>> + i + " Packet#000").getBytes();
>> + final byte[] response = new
>> byte[sslConnector.getApplicationBufferSize()];
>> + + final ByteBuffer writeBB =
>> ByteBuffer.wrap(testData);
>> + final ByteBuffer readBB = ByteBuffer.wrap(response);
>> + + final CountDownLatch[] responseArrivedLatchHolder
>> = new CountDownLatch[1];
>> + final CountDownLatch[] handshakeDoneLatchHolder =
>> new CountDownLatch[1];
>> + CountDownLatch handshakeDoneLatch = new
>> CountDownLatch(1);
>> + handshakeDoneLatchHolder[0] = handshakeDoneLatch;
>> + callbackHandler = createCallbackHandler(controller,
>> sslConnector, + responseArrivedLatchHolder,
>> handshakeDoneLatchHolder, + writeBB, readBB);
>> + + try {
>> + sslConnector.connect(new
>> InetSocketAddress("localhost", PORT),
>> + callbackHandler);
>> + +
>> waitOnLatch(handshakeDoneLatch, 10, TimeUnit.SECONDS);
>> + assertTrue(sslConnector.isHandshakeDone());
>> + + for(int j=0; j<PACKETS_COUNT; j++) {
>> + //System.out.println("Packet#" + j);
>> + CountDownLatch responseArrivedLatch = new
>> CountDownLatch(1);
>> + responseArrivedLatchHolder[0] =
>> responseArrivedLatch;
>> + readBB.clear();
>> + writeBB.position(writeBB.limit() - 3);
>> + byte[] packetNum =
>> Integer.toString(j).getBytes();
>> + writeBB.put(packetNum);
>> + writeBB.position(0);
>> + long nWrite = sslConnector.write(writeBB,
>> false);
>> + long nRead = -1;
>> + + // All bytes written
>> + if (nWrite == testData.length){
>> + nRead = sslConnector.read(readBB, false);
>> + }
>> + + if (nRead !=
>> nWrite){
>> + waitOnLatch(responseArrivedLatch, 15,
>> TimeUnit.SECONDS);
>> + }
>> + String val1 = new String(testData);
>> + String val2 = new String(toArray(readBB));
>> + //System.out.println("Assert. client#" + i
>> + " packet#" + j + " Pattern: " + val1 + " Came: " + val2);
>> + assertEquals(val1, val2);
>> + }
>> + } finally {
>> + sslConnector.close();
>> + controller.releaseConnectorHandler(sslConnector);
>> + }
>> + }
>> + } finally {
>> + try{
>> + controller.stop();
>> + } catch (Throwable t){
>> + t.printStackTrace();
>> + }
>> + }
>> + }
>> + + private Controller createSSLController(SSLContext sslContext) {
>> + final SSLReadFilter readFilter = new SSLReadFilter();
>> + readFilter.setSSLContext(sslContext);
>> + + final ProtocolFilter echoFilter = new SSLEchoFilter();
>> + + SSLSelectorHandler selectorHandler = new
>> SSLSelectorHandler();
>> + selectorHandler.setPort(PORT);
>> + + final Controller controller = new Controller();
>> + + controller.setSelectorHandler(selectorHandler);
>> + + controller.setProtocolChainInstanceHandler(new
>> DefaultProtocolChainInstanceHandler() {
>> + + public ProtocolChain poll() {
>> + ProtocolChain protocolChain = protocolChains.poll();
>> + if (protocolChain == null) {
>> + protocolChain = new DefaultProtocolChain();
>> + protocolChain.addFilter(readFilter);
>> + protocolChain.addFilter(echoFilter);
>> + }
>> + return protocolChain;
>> + }
>> + });
>> + + return controller;
>> + }
>> + + private SSLCallbackHandler createCallbackHandler(final
>> Controller controller, + final SSLConnectorHandler
>> sslConnector, + final CountDownLatch
>> responseArrivedLatch, + final CountDownLatch
>> handshakeDoneLatch, + final ByteBuffer writeBB, final
>> ByteBuffer readBB) {
>> + + return createCallbackHandler(controller,
>> sslConnector, + new CountDownLatch[]
>> {responseArrivedLatch},
>> + new CountDownLatch[] {handshakeDoneLatch},
>> + writeBB, readBB);
>> + }
>> + + private SSLCallbackHandler createCallbackHandler(final
>> Controller controller, + final SSLConnectorHandler
>> sslConnector, + final CountDownLatch[]
>> responseArrivedLatchHolder, + final CountDownLatch[]
>> handshakeDoneLatchHolder, + final ByteBuffer writeBB,
>> final ByteBuffer readBB) {
>> + + return new SSLCallbackHandler<Context>() {
>> + + private int readTry;
>> + + public void onConnect(IOEvent<Context> ioEvent) {
>> + SelectionKey key =
>> ioEvent.attachment().getSelectionKey();
>> + sslConnector.finishConnect(key);
>> + try {
>> + if (sslConnector.handshake(readBB, false)) {
>> + onHandshake(ioEvent);
>> + }
>> + } catch (IOException e) {
>> + e.printStackTrace();
>> + }
>> + }
>> + + public void onRead(IOEvent<Context> ioEvent) {
>> + try {
>> + long nRead = sslConnector.read(readBB, false);
>> + + if (nRead > 0 ||
>> readTry++ > 2) {
>> + responseArrivedLatchHolder[0].countDown();
>> + }
>> + } catch (IOException ex) {
>> + ex.printStackTrace();
>> +
>> controller.cancelKey(ioEvent.attachment().getSelectionKey());
>> + }
>> + }
>> + + public void onWrite(IOEvent<Context> ioEvent) {
>> + SelectionKey key =
>> ioEvent.attachment().getSelectionKey();
>> + try {
>> + while (writeBB.hasRemaining()) {
>> + long nWrite = sslConnector.write(writeBB,
>> false);
>> + + if (nWrite == 0) {
>> + return;
>> + }
>> + }
>> + } catch (IOException ex) {
>> + ex.printStackTrace();
>> + controller.cancelKey(key);
>> + }
>> + }
>> + + public void onHandshake(IOEvent<Context> ioEvent) {
>> + readBB.clear();
>> + handshakeDoneLatchHolder[0].countDown();
>> + }
>> + };
>> + }
>> + + public void waitOnLatch(CountDownLatch latch, int timeout,
>> TimeUnit timeUnit) {
>> + try {
>> + latch.await(timeout, timeUnit);
>> + } catch (InterruptedException ex) {
>> + ex.printStackTrace();
>> + }
>> + }
>> + + private byte[] toArray(ByteBuffer bb) {
>> + byte[] buf = new byte[bb.remaining()];
>> + bb.get(buf);
>> + return buf;
>> + }
>> + }
>> Index: main/java/com/sun/grizzly/SSLConnectorHandler.java
>> ***
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConnectorHandler.java
>> Locally New
>> ---
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConnectorHandler.java
>> Locally New
>> ***************
>> *** 1,0 ****
>> --- 1,905 ----
>> + /*
>> + * The contents of this file are subject to the terms
>> + * of the Common Development and Distribution License
>> + * (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/CDDLv1.0.html or
>> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * See the License for the specific language governing
>> + * permissions and limitations under the License.
>> + *
>> + * When distributing Covered Code, include this CDDL
>> + * Header Notice in each file and include the License file
>> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * If applicable, add the following below the CDDL Header,
>> + * with the fields enclosed by brackets [] replaced by
>> + * you own identifying information:
>> + * "Portions Copyrighted [year] [name of copyright owner]"
>> + *
>> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
>> + */
>> + package com.sun.grizzly;
>> + + import com.sun.grizzly.Controller.Protocol;
>> + import com.sun.grizzly.util.ByteBufferInputStream;
>> + import com.sun.grizzly.util.OutputWriter;
>> + import com.sun.grizzly.util.SSLOutputWriter;
>> + import com.sun.grizzly.util.SSLUtils;
>> + import java.io.EOFException;
>> + import java.io.IOException;
>> + import java.net.SocketAddress;
>> + import java.nio.ByteBuffer;
>> + import java.nio.channels.AlreadyConnectedException;
>> + import java.nio.channels.NotYetConnectedException;
>> + import java.nio.channels.SelectableChannel;
>> + import java.nio.channels.SelectionKey;
>> + import java.nio.channels.SocketChannel;
>> + import java.util.concurrent.CountDownLatch;
>> + import java.util.concurrent.TimeUnit;
>> + import javax.net.ssl.SSLContext;
>> + import javax.net.ssl.SSLEngine;
>> + import javax.net.ssl.SSLEngineResult;
>> + import javax.net.ssl.SSLEngineResult.HandshakeStatus;
>> + import javax.net.ssl.SSLException;
>> + + /**
>> + * Non blocking SSL Connector Handler. The recommended way to use
>> this class
>> + * is by creating an external Controller and share the same
>> SelectorHandler
>> + * instance.
>> + *
>> + * Recommended
>> + * -----------
>> + * Controller controller = new Controller();
>> + * // new SSLSelectorHandler(true) means the Selector will be used
>> only
>> + * // for client operation (OP_READ, OP_WRITE, OP_CONNECT).
>> + * SSLSelectorHandler sslSelectorHandler = new
>> SSLSelectorHandler(true);
>> + * controller.setSelectorHandler(sslSelectorHandler);
>> + * SSLConnectorHandler sslConnectorHandler = new
>> SSLConnectorHandler();
>> + * sslConnectorHandler.connect(localhost,port, new
>> SSLCallbackHandler(){...},
>> + * sslSelectorHandler);
>> + * SSLConnectorHandler sslConnectorHandler2 = new
>> SSLConnectorHandler();
>> + * sslConnectorHandler2.connect(localhost,port, new
>> SSLCallbackHandler(){...},
>> + * sslSelectorHandler);
>> + *
>> + * Not recommended (but still works)
>> + * ---------------------------------
>> + * SSLConnectorHandler sslConnectorHandler = new
>> SSLConnectorHandler();
>> + * sslConnectorHandler.connect(localhost,port);
>> + *
>> + * Internally, an new Controller will be created everytime
>> connect(localhost,port)
>> + * is invoked, which has an impact on performance.
>> + *
>> + * As common comment: developer should be very careful if dealing
>> directly with
>> + * <code>SSLConnectorHandler</code>'s underlying socket channel! In
>> most cases
>> + * there is no need to do this, but use read, write methods
>> provided + * by <code>SSLConnectorHandler</code>
>> + * + * @author Alexey Stashok
>> + * @author Jeanfrancois Arcand
>> + */
>> + public class SSLConnectorHandler implements
>> ConnectorHandler<SSLSelectorHandler, SSLCallbackHandler>,
>> CallbackHandler {
>> + + /**
>> + * The underlying SSLSelectorHandler used to mange SelectionKeys.
>> + */
>> + private SSLSelectorHandler selectorHandler;
>> + + + /**
>> + * A blocking <code>InputStream</code> that use a pool of Selector
>> + * to execute a blocking read operation.
>> + */
>> + private ByteBufferInputStream inputStream;
>> + + /**
>> + * A <code>SSLCallbackHandler</code> handler invoked by the
>> TCPSelectorHandler
>> + * when a non blocking operation is ready to be processed.
>> + */
>> + private SSLCallbackHandler callbackHandler;
>> + + /*
>> + * An empty ByteBuffer used for handshaking
>> + */
>> + private static final ByteBuffer EMPTY_BUFFER =
>> ByteBuffer.allocate(0);
>> + + /**
>> + * Input buffer for reading encrypted data from channel
>> + */
>> + private ByteBuffer securedInputBuffer;
>> + + /**
>> + * Output buffer, which contains encrypted data ready for
>> writing to channel
>> + */
>> + private ByteBuffer securedOutputBuffer;
>> + + /**
>> + * Buffer, where application data could be written during a
>> asynchronous handshaking.
>> + * It is set when user application calls:
>> SSLConnectorHandler.handshake(appDataBuffer)
>> + * and references appDataBuffer.
>> + */
>> + private ByteBuffer asyncHandshakeBuffer;
>> + + /**
>> + * The connection's SocketChannel.
>> + */
>> + private SocketChannel socketChannel;
>> + + /**
>> + * Default <code>SSLContext</code>, created on + * top of
>> default <code>SSLConfiguration</code>
>> + */
>> + private static volatile SSLContext defaultSSLContext;
>> + + /**
>> + * Is the connection established.
>> + */
>> + private volatile boolean isConnected;
>> + + /**
>> + * Is the handshake phase completed
>> + */
>> + private volatile boolean isHandshakeDone;
>> + + /**
>> + * The internal Controller used (in case not specified).
>> + */
>> + private Controller controller;
>> + + /**
>> + * IsConnected Latch related
>> + */
>> + private CountDownLatch isConnectedLatch;
>> + + /**
>> + * Are we creating a controller every run.
>> + */
>> + private boolean isStandalone = false;
>> + + /**
>> + * Is async handshake in progress
>> + */
>> + private boolean isProcessingAsyncHandshake;
>> + + /**
>> + * Result of last <code>SSLEngine</code> operation
>> + */
>> + private SSLEngineResult sslLastOperationResult;
>> + + /**
>> + * Current handshake status
>> + */
>> + private SSLEngineResult.HandshakeStatus handshakeStatus;
>> + + /**
>> + * Current <code>SSLEngine</code> status
>> + */
>> + private SSLEngineResult.Status sslEngineStatus = null;
>> + + + /**
>> + * Are we creating a controller every run.
>> + */
>> + private boolean delegateSSLTasks;
>> + + /**
>> + * Connector's <code>SSLEngine</code>
>> + */
>> + private SSLEngine sslEngine;
>> + + /**
>> + * Connector's <code>SSLContext</code>
>> + */
>> + private SSLContext sslContext;
>> + + public SSLConnectorHandler() {
>> + this(defaultSSLContext);
>> + }
>> + + public SSLConnectorHandler(SSLConfig sslConfig) {
>> + this(sslConfig.createSSLContext());
>> + }
>> + + public SSLConnectorHandler(SSLContext sslContext) {
>> + if (sslContext == null) {
>> + if (defaultSSLContext == null) {
>> + synchronized (SSLConnectorHandler.class) {
>> + if (defaultSSLContext == null) {
>> + defaultSSLContext =
>> SSLConfig.DEFAULT_CONFIG.createSSLContext();
>> + }
>> + }
>> + }
>> + + sslContext = defaultSSLContext;
>> + }
>> + + this.sslContext = sslContext;
>> + }
>> + + public boolean getDelegateSSLTasks() {
>> + return delegateSSLTasks;
>> + }
>> + + public void setDelegateSSLTasks(boolean delegateSSLTasks) {
>> + this.delegateSSLTasks = delegateSSLTasks;
>> + }
>> + + /**
>> + * Connect to hostname:port. When an aysnchronous event happens
>> (e.g
>> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
>> + * the CallBackHandler.
>> + * @param remoteAddress remote address to connect
>> + * @param callbackHandler the handler invoked by the Controller
>> when
>> + * an non blocking operation is ready to be handled.
>> + * @throws java.io.IOException
>> + */
>> + public void connect(SocketAddress remoteAddress,
>> SSLCallbackHandler callbackHandler) throws IOException {
>> + connect(remoteAddress, null, callbackHandler);
>> + }
>> + + /**
>> + * Connect to hostname:port. When an aysnchronous event happens
>> (e.g
>> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
>> + * the CallBackHandler.
>> + * @param remoteAddress remote address to connect
>> + * @param localAddress local address to bind
>> + * @param callbackHandler the handler invoked by the Controller
>> when
>> + * an non blocking operation is ready to be handled.
>> + * @throws java.io.IOException
>> + */
>> + public void connect(SocketAddress remoteAddress, SocketAddress
>> localAddress, SSLCallbackHandler callbackHandler) throws IOException {
>> + if (controller == null) {
>> + throw new IllegalStateException("Controller cannot be
>> null");
>> + }
>> + + connect(remoteAddress, localAddress, callbackHandler,
>> (SSLSelectorHandler) controller.getSelectorHandler(protocol()));
>> + }
>> + + /**
>> + * Connect to hostname:port. When an aysnchronous event happens
>> (e.g
>> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
>> + * the CallBackHandler.
>> + * @param remoteAddress remote address to connect
>> + * @param callbackHandler the handler invoked by the Controller
>> when
>> + * an non blocking operation is ready to be handled.
>> + * @param selectorHandler an instance of SelectorHandler.
>> + * @throws java.io.IOException
>> + */
>> + public void connect(SocketAddress remoteAddress,
>> SSLCallbackHandler callbackHandler, SSLSelectorHandler
>> selectorHandler) throws IOException {
>> + connect(remoteAddress, null, callbackHandler,
>> selectorHandler);
>> + }
>> + + /**
>> + * Connect to hostname:port. When an aysnchronous event happens
>> (e.g
>> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
>> + * the CallBackHandler.
>> + * @param remoteAddress remote address to connect
>> + * @param localAddress local address to bin
>> + * @param callbackHandler the handler invoked by the Controller
>> when
>> + * an non blocking operation is ready to be handled.
>> + * @param selectorHandler an instance of SelectorHandler.
>> + * @throws java.io.IOException
>> + */
>> + public void connect(SocketAddress remoteAddress, SocketAddress
>> localAddress, SSLCallbackHandler callbackHandler, SSLSelectorHandler
>> selectorHandler) throws IOException {
>> + if (isConnected) {
>> + throw new AlreadyConnectedException();
>> + }
>> + + if (controller == null) {
>> + throw new IllegalStateException("Controller cannot be
>> null");
>> + }
>> + + if (selectorHandler == null) {
>> + throw new IllegalStateException("Controller cannot be
>> null");
>> + }
>> + + this.selectorHandler = selectorHandler;
>> + this.callbackHandler = callbackHandler;
>> + + // Wait for the onConnect to be invoked.
>> + isConnectedLatch = new CountDownLatch(1);
>> + + selectorHandler.connect(remoteAddress, localAddress, this);
>> + inputStream = new ByteBufferInputStream();
>> + inputStream.setSecure(true);
>> + + try {
>> + isConnectedLatch.await(30, TimeUnit.SECONDS);
>> + } catch (InterruptedException ex) {
>> + throw new IOException(ex.getMessage());
>> + }
>> + }
>> + + /**
>> + * Connect to hostname:port. Internally an instance of
>> Controller and
>> + * its default SelectorHandler will be created everytime this
>> method is
>> + * called. This method should be used only and only if no external
>> + * Controller has been initialized.
>> + * @param remoteAddress remote address to connect
>> + * @throws java.io.IOException
>> + */
>> + public void connect(SocketAddress remoteAddress) throws
>> IOException {
>> + connect(remoteAddress, (SocketAddress) null);
>> + }
>> + + /**
>> + * Connect to hostname:port. Internally an instance of
>> Controller and
>> + * its default SelectorHandler will be created everytime this
>> method is
>> + * called. This method should be used only and only if no external
>> + * Controller has been initialized.
>> + * @param remoteAddress remote address to connect
>> + * @throws java.io.IOException
>> + * @param localAddress local address to bin
>> + */
>> + public void connect(SocketAddress remoteAddress, SocketAddress
>> localAddress) throws IOException {
>> + if (isConnected) {
>> + throw new AlreadyConnectedException();
>> + }
>> + + if (controller == null) {
>> + isStandalone = true;
>> + controller = new Controller();
>> + controller.setSelectorHandler(new
>> TCPSelectorHandler(true));
>> + DefaultPipeline pipeline = new DefaultPipeline();
>> + pipeline.initPipeline();
>> + pipeline.startPipeline();
>> + controller.setPipeline(pipeline);
>> + + callbackHandler = new SSLCallbackHandler<Context>() {
>> + + public void onConnect(IOEvent<Context> ioEvent) {
>> + SelectionKey key =
>> ioEvent.attachment().getSelectionKey();
>> + socketChannel = (SocketChannel) key.channel();
>> + finishConnect(key);
>> + getController().registerKey(key,
>> SelectionKey.OP_WRITE, Protocol.TCP);
>> + }
>> + + public void onRead(IOEvent<Context> ioEvent) {
>> + }
>> + + public void onWrite(IOEvent<Context> ioEvent) {
>> + }
>> + + public void onHandshake(IOEvent<Context> ioEvent) {
>> + }
>> + };
>> + + final CountDownLatch latch = new CountDownLatch(1);
>> + try {
>> + pipeline.execute(new Context() {
>> + @Override
>> + public Object call() throws Exception {
>> + latch.countDown();
>> + controller.start();
>> + return null;
>> + }
>> + });
>> + } catch (PipelineFullException ex) {
>> + throw new IOException(ex.getMessage());
>> + }
>> + + try {
>> + latch.await();
>> + Thread.sleep(1000);
>
> Just to make sure, can you make that variable configurable in case
> some platform gives us surprise?
>
>
>> + } catch (InterruptedException ex) {
>> + }
>> + }
>> + + connect(remoteAddress, localAddress, callbackHandler,
>> (SSLSelectorHandler) controller.getSelectorHandler(protocol()));
>> + }
>> + + /**
>> + * Initiate SSL handshake phase.
>> + * Handshake is required to be done after connection established.
>> + * + * @param byteBuffer Application
>> <code>ByteBuffer</code>, where application data
>> + * will be stored
>> + * @param blocking true, if handshake should be done in
>> blocking mode, for non-blocking false
>> + * @return If blocking parameter is true - method should always
>> return true if handshake is done,
>> + * or throw IOException otherwise. For non-blocking mode method
>> returns true if handshake is done, or false
>> + * if handshake will be completed in non-blocking manner.
>> + * @throws java.io.IOException if some error occurs during
>> processing I/O operations/
>> + */
>> + public boolean handshake(ByteBuffer byteBuffer, boolean
>> blocking) throws IOException {
>> + sslEngine.beginHandshake();
>> + handshakeStatus = sslEngine.getHandshakeStatus();
>> + + if (blocking) {
>> + SSLUtils.doHandshake(socketChannel, byteBuffer,
>> securedInputBuffer, securedOutputBuffer, sslEngine, handshakeStatus);
>> + finishHandshake();
>> + + // Sync should be always done
>> + return true;
>> + } else {
>> + doAsyncHandshake(byteBuffer);
>> + + // is async handshake completed
>> + return isHandshakeDone();
>> + }
>> + }
>> + + /**
>> + * Read bytes. If blocking is set to <tt>true</tt>, a pool of
>> temporary
>> + * <code>Selector</code> will be used to read bytes.
>> + * @param byteBuffer The byteBuffer to store bytes.
>> + * @param blocking <tt>true</tt> if a a pool of temporary Selector
>> + * is required to handle a blocking read.
>> + * @return number of bytes read
>> + * @throws java.io.IOException
>> + */
>> + public long read(ByteBuffer byteBuffer, boolean blocking)
>> throws IOException {
>> + if (!isConnected) {
>> + throw new NotYetConnectedException();
>> + }
>> + + if (blocking) {
>> + inputStream.setSelectionKey(getSelectionKey());
>> + return inputStream.read(byteBuffer);
>> + } else {
>> + int nRead = doReadAsync(byteBuffer);
>> + + if (!blocking && nRead == 0) {
>> + registerSelectionKeyFor(SelectionKey.OP_READ);
>> + return 0;
>> + }
>> + return nRead;
>> + }
>> + }
>> + + + /**
>> + * Writes bytes. If blocking is set to <tt>true</tt>, a pool of
>> temporary
>> + * <code>Selector</code> will be used to writes bytes.
>> + * @param byteBuffer The byteBuffer to write.
>> + * @param blocking <tt>true</tt> if a a pool of temporary Selector
>> + * is required to handle a blocking write.
>> + * @return number of bytes written. As on socket encrypted data
>> is written -
>> + * it's impossible to determine correspondent source data,
>> which was written.
>> + * That's why method either returns 0, if not all the source
>> data was written,
>> + * or byteBuffer.remaining() - if all the data was written
>> + * @throws java.io.IOException
>> + */
>> + public long write(ByteBuffer byteBuffer, boolean blocking)
>> throws IOException {
>> + if (!isConnected) {
>> + throw new NotYetConnectedException();
>> + }
>> + + if (blocking) {
>> + return SSLOutputWriter.flushChannel(socketChannel,
>> byteBuffer, securedOutputBuffer, sslEngine);
>> + } else {
>> + if (callbackHandler == null) {
>> + throw new IllegalStateException("Non blocking write
>> needs a CallbackHandler");
>> + }
>> + + int nWrite = 1;
>> + int totalWrite = 0;
>> + + while (nWrite > 0 && byteBuffer.hasRemaining()) {
>> + nWrite = doWriteAsync(byteBuffer);
>> + totalWrite += nWrite;
>> + }
>> + + if (totalWrite == 0 && byteBuffer.hasRemaining()) {
>> + registerSelectionKeyFor(SelectionKey.OP_WRITE);
>> + }
>> + return nWrite;
>> + }
>> + }
>> + + + /**
>> + * Close the underlying connection.
>> + */
>> + public void close() throws IOException {
>> + if (socketChannel != null) {
>> + try {
>> + if (securedOutputBuffer.hasRemaining()) {
>> + // if there is something is securedOutputBuffer
>> - flush it
>> + OutputWriter.flushChannel(socketChannel,
>> securedOutputBuffer);
>> + }
>> + + // Close secure outbound channel and flush data
>> + sslEngine.closeOutbound();
>> + SSLUtils.wrap(EMPTY_BUFFER, securedOutputBuffer,
>> sslEngine);
>> + OutputWriter.flushChannel(socketChannel,
>> securedOutputBuffer);
>> + } catch (IOException e) {
>> + }
>> +
>
> Log the exception as FINEST :-)
>
>> + if (selectorHandler != null) {
>> + SelectionKey key =
>> socketChannel.keyFor(selectorHandler.getSelector());
>> + + if (key == null) {
>> + return;
>> + }
>> + key.cancel();
>> + key.attach(null);
>> + }
>> + + socketChannel.close();
>> + }
>> + + if (controller != null && isStandalone) {
>> + controller.stop();
>> + controller = null;
>> + }
>> + + sslEngine = null;
>> + asyncHandshakeBuffer = null;
>> + isStandalone = false;
>> + isConnected = false;
>> + isHandshakeDone = false;
>> + }
>> + + + /**
>> + * Finish handling the OP_CONNECT interest ops.
>> + * @param key - a <code>SelectionKey</code>
>> + */
>> + public void finishConnect(SelectionKey key) {
>> + try {
>> + socketChannel = (SocketChannel) key.channel();
>> + socketChannel.finishConnect();
>> + isConnected = socketChannel.isConnected();
>> + initSSLEngineIfRequired();
>> + } catch (IOException ex) {
>> + // XXX LOG ME
>> + ex.printStackTrace();
>
> Log.
>
>> + } finally {
>> + isConnectedLatch.countDown();
>> + }
>> + }
>> + + /**
>> + * Changes SSLConnectorHandler state, after handshake operation
>> is done.
>> + * Normally should not be called by outside Grizzly, just in
>> case when custom
>> + * handshake code was used.
>> + */
>> + public void finishHandshake() {
>> + isProcessingAsyncHandshake = false;
>> + isHandshakeDone = true;
>> + }
>> + + /**
>> + * A token decribing the protocol supported by an
>> implementation of this
>> + * interface
>> + * @return this <code>ConnectorHandler</code>'s protocol
>> + */
>> + public Controller.Protocol protocol() {
>> + return Controller.Protocol.TLS;
>> + }
>> + + + /**
>> + * Is the underlying SocketChannel connected.
>> + * @return <tt>true</tt> if connected, otherwise <tt>false</tt>
>> + */
>> + public boolean isConnected() {
>> + return isConnected;
>> + }
>> + + /**
>> + * Is the underlying SocketChannel connected.
>> + * @return <tt>true</tt> if connected, otherwise <tt>false</tt>
>> + */
>> + public boolean isHandshakeDone() {
>> + return isHandshakeDone && !isProcessingAsyncHandshake;
>> + }
>> + + public Controller getController() {
>> + return controller;
>> + }
>> + + + public void setController(Controller controller) {
>> + this.controller = controller;
>> + }
>> + + public SelectableChannel getUnderlyingChannel() {
>> + return socketChannel;
>> + }
>> + + public SSLCallbackHandler getCallbackHandler() {
>> + return callbackHandler;
>> + }
>> + + public void setCallbackHandler(SSLCallbackHandler
>> callbackHandler) {
>> + this.callbackHandler = callbackHandler;
>> + }
>> + + public SSLSelectorHandler getSelectorHandler() {
>> + return selectorHandler;
>> + }
>> + + /**
>> + * Gets the size of the largest application buffer that may
>> occur when
>> + * using this session.
>> + * SSLEngine application data buffers must be large enough to
>> hold the
>> + * application data from any inbound network application data
>> packet
>> + * received. Typically, outbound application data buffers can
>> be of any size.
>> + *
>> + * (javadoc is taken from SSLSession.getApplicationBufferSize())
>> + * @return largets application buffer size, which may occur
>> + */
>> + public int getApplicationBufferSize() {
>> + initSSLEngineIfRequired();
>> + return sslEngine.getSession().getApplicationBufferSize();
>> + }
>> + + public void onConnect(IOEvent ioEvent) {
>> + callbackHandler.onConnect(ioEvent);
>> + }
>> + + public void onRead(IOEvent ioEvent) {
>> + try {
>> + // if processing handshake - pass the data to handshake
>> related code
>> + if (isProcessingAsyncHandshake) {
>> + doAsyncHandshake(asyncHandshakeBuffer);
>> + if (isHandshakeDone()) {
>> + callbackHandler.onHandshake(ioEvent);
>> + } else {
>> + return;
>> + }
>> + }
>> + + callbackHandler.onRead(ioEvent);
>> + } catch (IOException ex) {
>> + //log
>> + }
>> + }
>> + + public void onWrite(IOEvent ioEvent) {
>> + try {
>> + // check if all the secured data was written, if not -
>> + // flush as much as possible.
>> + if (!securedOutputBuffer.hasRemaining() ||
>> flushSecuredOutputBuffer()) {
>> + // if no encrypted data left in buffer - continue
>> processing
>> + if (isProcessingAsyncHandshake) {
>> + doAsyncHandshake(asyncHandshakeBuffer);
>> + if (isHandshakeDone()) {
>> + callbackHandler.onHandshake(ioEvent);
>> + } else {
>> + return;
>> + }
>> + }
>> + + callbackHandler.onWrite(ioEvent);
>> + }
>> + } catch (IOException ex) {
>> + // log
>> + }
>> + }
>> + + /**
>> + * Read a data from channel in async mode and decrypt
>> + * @param byteBuffer buffer for decrypted data
>> + * @return number of bytes read to byteBuffer
>> + * @throws java.io.IOException + */
>> + private int doReadAsync(ByteBuffer byteBuffer) throws
>> IOException {
>> + // Clear or compact secured input buffer
>> + clearOrCompactBuffer(securedInputBuffer);
>> + + // Read data to secured buffer
>> + int bytesRead = socketChannel.read(securedInputBuffer);
>> + + if (bytesRead == -1) {
>> + try {
>> + sslEngine.closeInbound();
>> + // check if there is some secured data still available
>> + if (securedInputBuffer.position() == 0 ||
>> + sslEngineStatus ==
>> SSLEngineResult.Status.BUFFER_UNDERFLOW) {
>> + return -1;
>> + }
>> + } catch (SSLException e) {
>> + return -1;
>> + }
>> + }
>> + + securedInputBuffer.flip();
>> + + if (bytesRead == 0 && !securedInputBuffer.hasRemaining()) {
>> + return 0;
>> + }
>> + + clearOrCompactBuffer(byteBuffer);
>> + + SSLEngineResult result = null;
>> + do {
>> + result = sslEngine.unwrap(securedInputBuffer, byteBuffer);
>> + // During handshake phase several unwrap actions could
>> be executed on read data
>> + } while (result.getStatus() == SSLEngineResult.Status.OK &&
>> result.getHandshakeStatus() ==
>> SSLEngineResult.HandshakeStatus.NEED_UNWRAP && result.bytesProduced()
>> == 0);
>> + + updateSSLEngineStatus(result);
>> + + if (sslEngineStatus == SSLEngineResult.Status.CLOSED) {
>> + return -1;
>> + } else if (sslEngineStatus ==
>> SSLEngineResult.Status.BUFFER_OVERFLOW) {
>> + throw new IllegalStateException("Application buffer is
>> overflowed");
>> + }
>> + + byteBuffer.flip();
>> + + return result.bytesProduced();
>> + }
>> + + /**
>> + * Write secured data to channel in async mode
>> + * + * @param byteBuffer non-crypted data buffer
>> + * @return number of bytes written. As non-crypted data is
>> passed, but crypted
>> + * data is written on channel - it's difficult to determine
>> correspondent size
>> + * of non-crypted data which was written. That's why method
>> returns 0, if not all
>> + * the data was written, or byteBuffer.remaining() if all the
>> source data was written.
>> + * @throws java.io.IOException + */
>> + private int doWriteAsync(ByteBuffer byteBuffer) throws
>> IOException {
>> + if (securedOutputBuffer.hasRemaining() &&
>> !flushSecuredOutputBuffer()) {
>> + return 0;
>> + }
>> + + securedOutputBuffer.clear();
>> + SSLEngineResult result = SSLUtils.wrap(byteBuffer,
>> securedOutputBuffer, sslEngine);
>> + + updateSSLEngineStatus(result);
>> + + int bytesWritten = socketChannel.write(securedOutputBuffer);
>> + if (securedOutputBuffer.hasRemaining()) {
>> + return 0;
>> + }
>> + + return result.bytesConsumed();
>> + }
>> + + /**
>> + * Perform an SSL handshake in async mode.
>> + * @param byteBuffer The application <code>ByteBuffer</code>
>> + *
>> + * @throws IOException if the handshake fail.
>> + */
>> + private void doAsyncHandshake(ByteBuffer byteBuffer) throws
>> IOException {
>> + SSLEngineResult result;
>> + isProcessingAsyncHandshake = true;
>> + asyncHandshakeBuffer = byteBuffer;
>> + while (handshakeStatus != HandshakeStatus.FINISHED) {
>> + switch (handshakeStatus) {
>> + case NEED_WRAP:
>> + result = SSLUtils.wrap(EMPTY_BUFFER,
>> securedOutputBuffer, sslEngine);
>> + updateSSLEngineStatus(result);
>> + switch (result.getStatus()) {
>> + case OK:
>> + if (!flushSecuredOutputBuffer()) {
>> + return;
>> + }
>> + break;
>> + default:
>> + throw new IOException("Handshaking
>> error: " + result.getStatus());
>> + }
>> + + if (handshakeStatus !=
>> HandshakeStatus.NEED_UNWRAP) {
>> + break;
>> + }
>> + case NEED_UNWRAP:
>> + int bytesRead = doReadAsync(byteBuffer);
>> + if (bytesRead == -1) {
>> + try {
>> + sslEngine.closeInbound();
>> + } catch (IOException ex) {
>> + }
>> + + throw new EOFException("Connection closed");
>> + } else if (bytesRead == 0 &&
>> sslLastOperationResult.bytesConsumed() == 0) {
>> + registerSelectionKeyFor(SelectionKey.OP_READ);
>> + return;
>> + }
>> + + if (handshakeStatus !=
>> HandshakeStatus.NEED_TASK) {
>> + break;
>> + }
>> + case NEED_TASK:
>> + handshakeStatus = executeDelegatedTask();
>> + break;
>> + default:
>> + throw new RuntimeException("Invalid Handshaking
>> State" + handshakeStatus);
>> + }
>> + }
>> + + if (isProcessingAsyncHandshake) {
>> + finishHandshake();
>> + }
>> + + asyncHandshakeBuffer = null;
>> + }
>> + + /**
>> + * Complete hanshakes operations.
>> + * @return SSLEngineResult.HandshakeStatus
>> + */
>> + private SSLEngineResult.HandshakeStatus executeDelegatedTask() {
>> + Runnable runnable;
>> + while ((runnable = sslEngine.getDelegatedTask()) != null) {
>> + runnable.run();
>> + }
>> + + return sslEngine.getHandshakeStatus();
>> + }
>> + + /**
>> + * Update <code>SSLConnectorHandler</code> internal status with
>> + * last <code>SSLEngine</code> operation result.
>> + * + * @param result last <code>SSLEngine</code> operation
>> result
>> + */
>> + private void updateSSLEngineStatus(SSLEngineResult result) {
>> + sslLastOperationResult = result;
>> + sslEngineStatus = result.getStatus();
>> + handshakeStatus = result.getHandshakeStatus();
>> + }
>> + + /**
>> + * Clears buffer if there is no info available, or compact
>> buffer otherwise.
>> + * @param buffer byte byffer
>> + */
>> + private static void clearOrCompactBuffer(ByteBuffer buffer) {
>> + if (!buffer.hasRemaining()) {
>> + buffer.clear();
>> + } else if (buffer.position() > 0) {
>> + buffer.compact();
>> + }
>> + }
>> + + /**
>> + * Gets <code>SSLConnectorHandler</code> <code>SelectionKey</code>
>> + * @return <code>SelectionKey</code>
>> + */
>> + private SelectionKey getSelectionKey() {
>> + return socketChannel.keyFor(selectorHandler.getSelector());
>> + }
>> + + /**
>> + * Registers <code>SSLConnectorHandler<code>'s
>> <code>SelectionKey</code>
>> + * to listen channel operations.
>> + * @param ops interested channel operations
>> + */
>> + private void registerSelectionKeyFor(int ops) {
>> + SelectionKey key = getSelectionKey();
>> + key.attach(this);
>> + selectorHandler.register(key, ops);
>> + }
>> + + /**
>> + * Flushes as much as possible bytes from the secured output
>> buffer
>> + * @return true if secured buffer was completely flushed, false
>> otherwise
>> + */
>> + private boolean flushSecuredOutputBuffer() throws IOException {
>> + int nWrite = 1;
>> + + while (nWrite > 0 && securedOutputBuffer.hasRemaining()) {
>> + nWrite = socketChannel.write(securedOutputBuffer);
>> + }
>> + + if (securedOutputBuffer.hasRemaining()) {
>> + SelectionKey key =
>> socketChannel.keyFor(selectorHandler.getSelector());
>> + key.attach(callbackHandler);
>> + selectorHandler.register(key, SelectionKey.OP_WRITE);
>> + + return false;
>> + }
>> + + return true;
>> + }
>> + + /**
>> + * Initiate <code>SSLEngine</code> and related secure buffers
>> + */
>> + private void initSSLEngineIfRequired() {
>> + if (sslEngine == null) {
>> + sslEngine = sslContext.createSSLEngine();
>> + sslEngine.setUseClientMode(true);
>> + + int bbSize =
>> sslEngine.getSession().getPacketBufferSize();
>> + securedInputBuffer = ByteBuffer.allocate(bbSize * 2);
>> + securedOutputBuffer = ByteBuffer.allocate(bbSize * 2);
>> + }
>> + }
>
> Might be a stupid question, but here you aren't configuring the
> protocols and the certs. Can clients set those as well? I suspect yes,
> but just double checking.
>
>
>
>> + }
>> Index: test/java/com/sun/grizzly/utils/SSLEchoFilter.java
>> ***
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\utils\SSLEchoFilter.java
>> Locally New
>> ---
>> C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\utils\SSLEchoFilter.java
>> Locally New
>> ***************
>> *** 1,0 ****
>> --- 1,66 ----
>> + /*
>> + * The contents of this file are subject to the terms
>> + * of the Common Development and Distribution License
>> + * (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/CDDLv1.0.html or
>> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * See the License for the specific language governing
>> + * permissions and limitations under the License.
>> + *
>> + * When distributing Covered Code, include this CDDL
>> + * Header Notice in each file and include the License file
>> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> + * If applicable, add the following below the CDDL Header,
>> + * with the fields enclosed by brackets [] replaced by
>> + * you own identifying information:
>> + * "Portions Copyrighted [year] [name of copyright owner]"
>> + *
>> + * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
>> + */
>> + + package com.sun.grizzly.utils;
>> + + import com.sun.grizzly.*;
>> + import com.sun.grizzly.util.SSLOutputWriter;
>> + import com.sun.grizzly.util.WorkerThread;
>> + import java.io.IOException;
>> + import java.nio.ByteBuffer;
>> + import java.nio.channels.SocketChannel;
>> + + /**
>> + * Simple echo filter
>> + *
>> + * @author Alexey Stashok
>> + */
>> + public class SSLEchoFilter implements ProtocolFilter {
>> + public boolean execute(Context ctx) throws IOException {
>> + final WorkerThread workerThread =
>> ((WorkerThread)Thread.currentThread());
>> + ByteBuffer buffer = workerThread.getByteBuffer();
>> + buffer.flip();
>> + if (buffer.hasRemaining()) {
>> + // Store incoming data in byte[]
>> + byte[] data = new byte[buffer.remaining()];
>> + int position = buffer.position();
>> + buffer.get(data);
>> + buffer.position(position);
>> + + SocketChannel channel = (SocketChannel)
>> ctx.getSelectionKey().channel();
>> + try {
>> + SSLOutputWriter.flushChannel(channel, buffer);
>> + } catch (IOException ex) {
>> + System.out.println("Exception. Echo \"" + new
>> String(data) + "\"");
>> + throw ex;
>> + }
>> + }
>> + + buffer.clear();
>> + return false;
>> + }
>> + + public boolean postExecute(Context ctx) throws IOException {
>> + return true;
>> + }
>> + }
>>
>>
>>
>> Index: test/java/com/sun/grizzly/TCPConnectorHandlerTest.java
>> ===================================================================
>> --- test/java/com/sun/grizzly/TCPConnectorHandlerTest.java
>> (revision 329)
>> +++ test/java/com/sun/grizzly/TCPConnectorHandlerTest.java
>> (working copy)
>> @@ -22,7 +22,6 @@
>> */
>> package com.sun.grizzly;
>>
>> -import
>> com.sun.grizzly.connectioncache.client.CacheableConnectorHandlerPool;
>> import com.sun.grizzly.filter.ReadFilter;
>> import com.sun.grizzly.utils.ControllerUtils;
>> import com.sun.grizzly.utils.EchoFilter;
>> Index: main/java/com/sun/grizzly/TCPConnectorHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/TCPConnectorHandler.java (revision 329)
>> +++ main/java/com/sun/grizzly/TCPConnectorHandler.java (working copy)
>> @@ -65,7 +65,7 @@
>> *
>> * @author Jeanfrancois Arcand
>> */
>> -public class TCPConnectorHandler implements
>> ConnectorHandler<TCPSelectorHandler>{
>> +public class TCPConnectorHandler implements
>> ConnectorHandler<TCPSelectorHandler, CallbackHandler>{
>> /**
>> * The underlying TCPSelectorHandler used to mange SelectionKeys.
>> Index: main/java/com/sun/grizzly/IOEvent.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/IOEvent.java (revision 329)
>> +++ main/java/com/sun/grizzly/IOEvent.java (working copy)
>> @@ -48,4 +48,23 @@
>> */
>> public E attachment();
>>
>> + /**
>> + * Simple IOEvent implementation
>> + */
>> + public class DefaultIOEvent<E> implements IOEvent<E> {
>> + private E attachment;
>> +
>> + public DefaultIOEvent(E attachment) {
>> + this.attachment = attachment;
>> + }
>> +
>> + public E attach(E attachment) {
>> + this.attachment = attachment;
>> + return attachment;
>> + }
>> +
>> + public E attachment() {
>> + return attachment;
>> + }
>> + }
>> }
>> Index: main/java/com/sun/grizzly/ConnectorInstanceHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/ConnectorInstanceHandler.java
>> (revision 329)
>> +++ main/java/com/sun/grizzly/ConnectorInstanceHandler.java
>> (working copy)
>> @@ -23,6 +23,7 @@
>>
>> package com.sun.grizzly;
>>
>> +import java.util.concurrent.Callable;
>> import java.util.concurrent.ConcurrentLinkedQueue;
>>
>> /**
>> @@ -85,4 +86,26 @@
>> public abstract E newInstance();
>> }
>> + /**
>> + * Concurrent Queue ConnectorInstanceHandler implementation
>> + * @param E ConnectorHandler implementation this pool will manage
>> + */
>> + public class ConcurrentQueueDelegateCIH<E extends
>> ConnectorHandler> + extends
>> ConcurrentQueueConnectorInstanceHandler<E> {
>> + + // ConnectorHandler instance creator
>> + private Callable<E> delegate;
>> + + public ConcurrentQueueDelegateCIH(Callable<E>
>> delegate) {
>> + this.delegate = delegate;
>> + }
>> + + public E newInstance() {
>> + try {
>> + return delegate.call();
>> + } catch(Exception e) {
>> + throw new IllegalStateException("Unexpected
>> exception");
>> + }
>> + }
>> + }
>> }
>> Index: main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java
>> (revision 329)
>> +++ main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java
>> (working copy)
>> @@ -1,38 +0,0 @@
>> -/*
>> - * The contents of this file are subject to the terms
>> - * of the Common Development and Distribution License
>> - * (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/CDDLv1.0.html or
>> - * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> - * See the License for the specific language governing
>> - * permissions and limitations under the License.
>> - *
>> - * When distributing Covered Code, include this CDDL
>> - * Header Notice in each file and include the License file
>> - * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> - * If applicable, add the following below the CDDL Header,
>> - * with the fields enclosed by brackets [] replaced by
>> - * you own identifying information:
>> - * "Portions Copyrighted [year] [name of copyright owner]"
>> - *
>> - * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
>> - */
>> -
>> -package com.sun.grizzly;
>> -
>> -/**
>> - * Default <code>ConnectorInstanceHandler</code> which use a - *
>> <code>List</code> to pool <code>ConnectorHandler</code>
>> - *
>> - * @author Jeanfrancois
>> - */
>> -public class UDPConnectorInstanceHandler extends -
>> ConnectorInstanceHandler.ConcurrentQueueConnectorInstanceHandler<UDPConnectorHandler>
>> {
>> - - public UDPConnectorHandler newInstance() {
>> - return new UDPConnectorHandler();
>> - }
>> -}
>> Index: main/java/com/sun/grizzly/UDPSelectorHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/UDPSelectorHandler.java (revision 329)
>> +++ main/java/com/sun/grizzly/UDPSelectorHandler.java (working copy)
>> @@ -34,6 +34,7 @@
>> import java.nio.channels.SelectionKey;
>> import java.nio.channels.Selector;
>> import java.util.Iterator;
>> +import java.util.concurrent.Callable;
>> import java.util.concurrent.ConcurrentHashMap;
>> import java.util.concurrent.ConcurrentLinkedQueue;
>> import java.util.logging.Level;
>> @@ -92,7 +93,9 @@
>> public void preSelect(Context ctx) throws IOException {
>> if (selector == null){
>> try{
>> - connectorInstanceHandler = new
>> UDPConnectorInstanceHandler();
>> + connectorInstanceHandler = new
>> ConnectorInstanceHandler.
>> + ConcurrentQueueDelegateCIH(
>> + getConnectorInstanceHandlerDelegate());
>> datagramChannel = DatagramChannel.open();
>> datagramChannel.configureBlocking(false);
>> selector = Selector.open(); @@
>> -246,4 +249,14 @@
>> public void setSocketTimeout(int socketTimeout) {
>> throw new IllegalStateException(NOT_SUPPORTED);
>> } +
>> + //--------------- ConnectorInstanceHandler
>> -----------------------------
>> + @Override
>> + protected Callable<ConnectorHandler>
>> getConnectorInstanceHandlerDelegate() {
>> + return new Callable<ConnectorHandler>() {
>> + public ConnectorHandler call() throws Exception {
>> + return new UDPConnectorHandler();
>> + }
>> + };
>> + }
>> }
>> Index: main/java/com/sun/grizzly/ConnectorHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/ConnectorHandler.java (revision 329)
>> +++ main/java/com/sun/grizzly/ConnectorHandler.java (working copy)
>> @@ -41,7 +41,7 @@
>> * @param E a <code>SelectorHandler</code>
>> * @author Jeanfrancois Arcand
>> */
>> -public interface ConnectorHandler<E extends SelectorHandler> extends
>> Handler, Closeable {
>> +public interface ConnectorHandler<E extends SelectorHandler, P
>> extends CallbackHandler> extends Handler, Closeable {
>> /**
>> @@ -63,7 +63,7 @@
>> * @throws java.io.IOException */
>> public void connect(SocketAddress remoteAddress,
>> - CallbackHandler callbackHandler,
>> + P callbackHandler,
>> E e) throws IOException;
>>
>> @@ -77,7 +77,7 @@
>> * @throws java.io.IOException
>> */ public void connect(SocketAddress remoteAddress,
>> - CallbackHandler callbackHandler) throws
>> IOException;
>> + P callbackHandler) throws IOException;
>> /**
>> @@ -104,7 +104,7 @@
>> * @throws java.io.IOException
>> */
>> public void connect(SocketAddress remoteAddress, SocketAddress
>> localAddress, - CallbackHandler callbackHandler,
>> + P callbackHandler,
>> E e) throws IOException;
>>
>> @@ -119,7 +119,7 @@
>> * @throws java.io.IOException */ public void
>> connect(SocketAddress remoteAddress, SocketAddress localAddress,
>> - CallbackHandler callbackHandler) throws
>> IOException;
>> + P callbackHandler) throws IOException;
>> /**
>> @@ -205,7 +205,7 @@
>> * * @return callback handler
>> */
>> - public CallbackHandler getCallbackHandler();
>> + public P getCallbackHandler();
>> /**
>> * Sets <code>ConnectorHandler</code>'s callback handler instance,
>> @@ -213,5 +213,5 @@
>> * * @param callbackHandler handler
>> */
>> - public void setCallbackHandler(CallbackHandler callbackHandler);
>> + public void setCallbackHandler(P callbackHandler);
>> }
>> Index: main/java/com/sun/grizzly/UDPConnectorHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/UDPConnectorHandler.java (revision 329)
>> +++ main/java/com/sun/grizzly/UDPConnectorHandler.java (working copy)
>> @@ -46,7 +46,7 @@
>> * * @author Jeanfrancois Arcand
>> */
>> -public class UDPConnectorHandler implements
>> ConnectorHandler<UDPSelectorHandler>{
>> +public class UDPConnectorHandler implements
>> ConnectorHandler<UDPSelectorHandler, CallbackHandler>{
>> /**
>> * The underlying UDPSelectorHandler used to mange SelectionKeys.
>> Index:
>> main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java
>>
>> ===================================================================
>> ---
>> main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java
>> (revision 329)
>> +++
>> main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java
>> (working copy)
>> @@ -39,7 +39,7 @@
>> *
>> * @author Alexey Stashok
>> */
>> -public class CacheableConnectorHandler implements
>> ConnectorHandler<SelectorHandler>,
>> +public class CacheableConnectorHandler implements
>> ConnectorHandler<SelectorHandler, CallbackHandler>,
>> ContactInfo<ConnectorHandler>, CallbackHandler {
>> private SocketAddress targetAddress;
>> private Protocol protocol;
>> @@ -231,15 +231,7 @@
>> Selector protocolSelector =
>> underlyingConnectorHandler.getSelectorHandler().getSelector();
>> SelectionKey key =
>> underlyingConnectorHandler.getUnderlyingChannel().keyFor(protocolSelector);
>>
>> final Context context =
>> parentPool.getController().pollContext(key);
>> - onConnect(new IOEvent<Context>() {
>> - public Context attach(Context e) {
>> - return context;
>> - }
>> - - public Context attachment() {
>> - return context;
>> - }
>> - });
>> + onConnect(new IOEvent.DefaultIOEvent<Context>(context));
>> }
>> //---------------------- CallbackHandler implementation
>> --------------------------------
>> Index: main/java/com/sun/grizzly/filter/SSLReadFilter.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/filter/SSLReadFilter.java (revision
>> 329)
>> +++ main/java/com/sun/grizzly/filter/SSLReadFilter.java (working
>> copy)
>> @@ -114,7 +114,7 @@
>> /**
>> * Encrypted ByteBuffer default size.
>> */
>> - protected int inputBBSize = 5 * 4096; + protected int
>> inputBBSize = 5 * 4096;
>> public SSLReadFilter() {
>> @@ -177,7 +177,7 @@
>> * needs to be invoked.
>> */
>> public boolean postExecute(Context ctx) throws IOException {
>> - ctx.setProtocol(Controller.Protocol.TCP);
>> + ctx.setProtocol(Controller.Protocol.TLS);
>> if (ctx.getKeyRegistrationState()
>> == Context.KeyRegistrationState.CANCEL){
>> ctx.getController().cancelKey(ctx.getSelectionKey());
>> @@ -266,8 +266,8 @@
>> boolean OK = true; try{ byteBuffer
>> = SSLUtils.doHandshake
>> - (key,byteBuffer,inputBB,outputBB,sslEngine,
>> - handshakeStatus,timeout);
>> + ((SocketChannel) key.channel(), byteBuffer,
>> inputBB,
>> + outputBB, sslEngine, handshakeStatus, timeout);
>> if (doRead(key) == -1){
>> throw new EOFException();
>> }
>> @@ -313,7 +313,7 @@
>> } catch (SSLException ex){
>> ;
>> }
>> - } + }
>> }
>> }
>> Index: main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java
>> (revision 329)
>> +++ main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java
>> (working copy)
>> @@ -1,38 +0,0 @@
>> -/*
>> - * The contents of this file are subject to the terms
>> - * of the Common Development and Distribution License
>> - * (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/CDDLv1.0.html or
>> - * glassfish/bootstrap/legal/CDDLv1.0.txt.
>> - * See the License for the specific language governing
>> - * permissions and limitations under the License.
>> - *
>> - * When distributing Covered Code, include this CDDL
>> - * Header Notice in each file and include the License file
>> - * at glassfish/bootstrap/legal/CDDLv1.0.txt.
>> - * If applicable, add the following below the CDDL Header,
>> - * with the fields enclosed by brackets [] replaced by
>> - * you own identifying information:
>> - * "Portions Copyrighted [year] [name of copyright owner]"
>> - *
>> - * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
>> - */
>> -
>> -package com.sun.grizzly;
>> -
>> -/**
>> - * Default <code>ConnectorInstanceHandler</code> which use a - *
>> <code>List</code> to pool <code>ConnectorHandler</code>
>> - *
>> - * @author Jeanfrancois
>> - */
>> -public class TCPConnectorInstanceHandler extends
>> -
>> ConnectorInstanceHandler.ConcurrentQueueConnectorInstanceHandler<TCPConnectorHandler>
>> {
>> - - public TCPConnectorHandler newInstance() {
>> - return new TCPConnectorHandler();
>> - }
>> -}
>> Index: main/java/com/sun/grizzly/TCPSelectorHandler.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/TCPSelectorHandler.java (revision 329)
>> +++ main/java/com/sun/grizzly/TCPSelectorHandler.java (working copy)
>> @@ -39,6 +39,7 @@
>> import java.nio.channels.SocketChannel;
>> import java.util.Iterator;
>> import java.util.Set;
>> +import java.util.concurrent.Callable;
>> import java.util.concurrent.ConcurrentHashMap;
>> import java.util.concurrent.ConcurrentLinkedQueue;
>> import java.util.logging.Level;
>> @@ -232,7 +233,9 @@
>> public void preSelect(Context ctx) throws IOException {
>> if (selector == null){
>> try{
>> - connectorInstanceHandler = new
>> TCPConnectorInstanceHandler();
>> + connectorInstanceHandler = new
>> ConnectorInstanceHandler.
>> + ConcurrentQueueDelegateCIH(
>> + getConnectorInstanceHandlerDelegate());
>> // Create the socket listener
>> selector = Selector.open();
>> @@ -550,8 +553,7 @@
>> protected void invokeCallbackHandler(Context context) throws
>> IOException{
>> context.setProtocol(protocol());
>> - IOEvent<Context>ioEvent = createIOEvent();
>> - ioEvent.attach(context);
>> + IOEvent<Context>ioEvent = new
>> IOEvent.DefaultIOEvent<Context>(context);
>> context.setIOEvent(ioEvent);
>> try {
>> context.execute();
>> @@ -736,27 +738,8 @@
>> public void setReuseAddress(boolean reuseAddress) {
>> this.reuseAddress = reuseAddress;
>> }
>> - + /**
>> - * Create IOEvent instance
>> - * @return IOEvent
>> - */
>> - protected static <E> IOEvent<E> createIOEvent() {
>> - return new IOEvent<E>() {
>> - private E context;
>> - public E attach(E context){
>> - this.context = context;
>> - return context;
>> - }
>> - - public E attachment(){
>> - return context;
>> - }
>> - }; - }
>> - - - /**
>> * Return the Pipeline used to execute this SelectorHandler's
>> * SelectionKey ops
>> * @return The pipeline to use, or null if the Controller's
>> Pipeline
>> @@ -781,4 +764,18 @@
>> public void setSelectionKeyHandler(SelectionKeyHandler
>> selectionKeyHandler) {
>> this.selectionKeyHandler = selectionKeyHandler;
>> }
>> + + //--------------- ConnectorInstanceHandler
>> -----------------------------
>> + /**
>> + * Return <Callable>factory<Callable> object, which knows how
>> + * to create <code>ConnectorInstanceHandler<code> corresponding
>> to the protocol
>> + * @return <Callable>factory</code>
>> + */
>> + protected Callable<ConnectorHandler>
>> getConnectorInstanceHandlerDelegate() {
>> + return new Callable<ConnectorHandler>() {
>> + public ConnectorHandler call() throws Exception {
>> + return new TCPConnectorHandler();
>> + }
>> + };
>> + }
>> }
>> Index: main/java/com/sun/grizzly/util/SSLUtils.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/util/SSLUtils.java (revision 329)
>> +++ main/java/com/sun/grizzly/util/SSLUtils.java (working copy)
>> @@ -69,24 +69,23 @@
>> /**
>> * Read encrypted bytes using an <code>SSLEngine</code>.
>> - * @param key The SelectionKey
>> + * @param socketChannel The SocketChannel
>> * @param inputBB The byteBuffer to store encrypted bytes
>> * @param sslEngine The SSLEngine uses to manage the SSL
>> operations.
>> * @param timeout The Selector.select() timeout value. A value
>> of 0 will
>> * be exectuted as a Selector.selectNow();
>> * @return the bytes read.
>> */
>> - public static int doRead(SelectionKey key, ByteBuffer inputBB,
>> + public static int doRead(SocketChannel socketChannel, ByteBuffer
>> inputBB, SSLEngine sslEngine, int timeout){
>> - if (key == null) return -1;
>> + if (socketChannel == null) return -1;
>>
>> int count = 1;
>> int byteRead = 0;
>> Selector readSelector = null;
>> SelectionKey tmpKey = null;
>> try{
>> - SocketChannel socketChannel = (SocketChannel)key.channel();
>> while (count > 0){
>> count = socketChannel.read(inputBB);
>> @@ -290,7 +289,7 @@
>> /**
>> * Perform an SSL handshake using the SSLEngine. - * @param
>> key the <code>SelectionKey</code>
>> + * @param socketChannel the <code>SocketChannel</code>
>> * @param byteBuffer The application <code>ByteBuffer</code>
>> * @param inputBB The encrypted input <code>ByteBuffer</code>
>> * @param outputBB The encrypted output <code>ByteBuffer</code>
>> @@ -300,18 +299,18 @@
>> * @throws java.io.IOException * @throw IOException if the
>> handshake fail.
>> */
>> - public static ByteBuffer doHandshake(SelectionKey key,
>> + public static ByteBuffer doHandshake(SocketChannel socketChannel,
>> ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer
>> outputBB,
>> SSLEngine sslEngine, HandshakeStatus handshakeStatus)
>> throws IOException {
>> - return doHandshake(key,byteBuffer,inputBB,outputBB,sslEngine,
>> - handshakeStatus,readTimeout);
>> + return doHandshake(socketChannel, byteBuffer, inputBB,
>> outputBB,
>> + sslEngine, handshakeStatus, readTimeout);
>> }
>>
>> /**
>> * Perform an SSL handshake using the SSLEngine. - * @param
>> key the <code>SelectionKey</code>
>> + * @param socketChannel the <code>SocketChannel</code>
>> * @param byteBuffer The application <code>ByteBuffer</code>
>> * @param inputBB The encrypted input <code>ByteBuffer</code>
>> * @param outputBB The encrypted output <code>ByteBuffer</code>
>> @@ -320,9 +319,9 @@
>> * @param timeout * @return byteBuffer the new ByteBuffer
>> * @throws java.io.IOException - * @throw IOException if the
>> handshake fail.
>> + * @throws IOException if the handshake fail.
>> */
>> - public static ByteBuffer doHandshake(SelectionKey key,
>> + public static ByteBuffer doHandshake(SocketChannel socketChannel,
>> ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer
>> outputBB,
>> SSLEngine sslEngine, HandshakeStatus handshakeStatus,int
>> timeout) throws IOException {
>> @@ -332,7 +331,7 @@
>> while (handshakeStatus != HandshakeStatus.FINISHED){
>> switch (handshakeStatus) {
>> case NEED_UNWRAP:
>> - if (doRead(key,inputBB,sslEngine, timeout) <=
>> eof) {
>> + if (doRead(socketChannel,inputBB,sslEngine,
>> timeout) <= eof) {
>> try{
>> sslEngine.closeInbound();
>> } catch (IOException ex){
>> @@ -390,9 +389,9 @@
>> }
>>
>> // Flush all Server bytes to the client.
>> - if (key != null) {
>> + if (socketChannel != null) {
>> OutputWriter.flushChannel(
>> -
>> (SocketChannel)key.channel(), outputBB);
>> + socketChannel, outputBB);
>> outputBB.clear();
>> }
>> break;
>> Index: main/java/com/sun/grizzly/util/ByteBufferInputStream.java
>> ===================================================================
>> --- main/java/com/sun/grizzly/util/ByteBufferInputStream.java
>> (revision 329)
>> +++ main/java/com/sun/grizzly/util/ByteBufferInputStream.java
>> (working copy)
>> @@ -284,7 +284,7 @@
>> // bytes, the byteBuffer will be empty event if some
>> encrypted bytes
>> // are available. while (byteBuffer.position() ==
>> initialPosition){
>> - byteRead += SSLUtils.doRead(key,workerThread.getInputBB(),
>> + byteRead += SSLUtils.doRead((SocketChannel)
>> key.channel(), workerThread.getInputBB(),
>> workerThread.getSSLEngine(),readTimeout);
>>
>> if (byteRead > 0 || workerThread.getInputBB().position()
>> > 0) {
>> @@ -371,10 +371,9 @@
>> }
>> SelectorFactory.returnSelector(readSelector);
>> }
>> -
>> + byteBuffer.flip();
>> }
>> - return byteRead;
>> }
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>
--
Charlie Hunt
Java Performance Engineer
630.285.7708 x47708 (Internal)
<http://java.sun.com/docs/performance/>