/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.grizzly;
import com.sun.grizzly.filter.SSLEchoAsyncWriteQueueFilter;
import com.sun.grizzly.filter.SSLReadFilter;
import com.sun.grizzly.util.WorkerThread;
import com.sun.grizzly.utils.ControllerUtils;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import junit.framework.TestCase;
/**
*
* @author Alexey Stashok
*/
public class SSLAsyncQueueWriterTest extends TestCase {
private static Logger logger = Logger.getLogger("grizzly.test");
public static final int PORT = 17508;
public static final int PACKETS_COUNT = 5;
public static final int CLIENTS_COUNT = 100;
public static final int SIMULT_CLIENTS = 1000;
public static final int SIMULT_THREADS_COUNT = 20;
/**
* A {@link SSLCallbackHandler} handler invoked by the TCPSelectorHandler
* when a non blocking operation is ready to be processed.
*/
private SSLCallbackHandler callbackHandler;
private AtomicInteger echoBytesProcessed = new AtomicInteger(0);
private SSLConfig sslConfig;
@Override
public void setUp() throws URISyntaxException {
sslConfig = new SSLConfig();
ClassLoader cl = getClass().getClassLoader();
// override system properties
URL cacertsUrl = cl.getResource("ssltest-cacerts.jks");
String trustStoreFile = new File(cacertsUrl.toURI()).getAbsolutePath();
if (cacertsUrl != null) {
sslConfig.setTrustStoreFile(trustStoreFile);
sslConfig.setTrustStorePass("changeit");
}
logger.log(Level.INFO, "SSL certs path: " + trustStoreFile);
// override system properties
URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
String keyStoreFile = new File(keystoreUrl.toURI()).getAbsolutePath();
if (keystoreUrl != null) {
sslConfig.setKeyStoreFile(keyStoreFile);
sslConfig.setKeyStorePass("changeit");
}
logger.log(Level.INFO, "SSL keystore path: " + keyStoreFile);
SSLConfig.DEFAULT_CONFIG = sslConfig;
}
public void testSeveralPackets() throws IOException {
Controller.logger().log(Level.INFO, "Starting SSLAsyncQueueWriterTest.testSeveralPackets() test...");
final Controller controller = createSSLController(SSLConfig.DEFAULT_CONFIG.createSSLContext());
ControllerUtils.startController(controller);
try {
for(int i=0; i<CLIENTS_COUNT; i++) {
final SSLConnectorHandler sslConnector =
(SSLConnectorHandler) controller.acquireConnectorHandler(Controller.Protocol.TLS);
sslConnector.setController(controller);
final byte[] testData = new String("Hello. Client#" + i + " Packet#000").getBytes();
int appBufferSize = Math.max(sslConnector.getApplicationBufferSize(), testData.length * SIMULT_CLIENTS);
final byte[] response = new byte[appBufferSize + sslConnector.getApplicationBufferSize()];
final ByteBuffer writeBB = ByteBuffer.wrap(testData);
final ByteBuffer readBB = ByteBuffer.wrap(response);
CountDownLatch handshakeDoneLatch = new CountDownLatch(1);
final AtomicReference<CountDownLatch> responseArrivedLatchHolder = new AtomicReference<CountDownLatch>();
final AtomicReference<CountDownLatch> handshakeDoneLatchHolder = new AtomicReference<CountDownLatch>(handshakeDoneLatch);
callbackHandler = createCallbackHandler(controller, sslConnector,
responseArrivedLatchHolder, handshakeDoneLatchHolder,
writeBB, readBB);
try {
sslConnector.connect(new InetSocketAddress("localhost", PORT),
callbackHandler);
sslConnector.handshake(readBB, true);
assertTrue("Handshake is not completed! testString: " +
new String(testData) + " latchCounter: " +
handshakeDoneLatchHolder.get().getCount() +
" isConnected: " + sslConnector.isConnected(),
sslConnector.isHandshakeDone());
for(int j=0; j<PACKETS_COUNT; j++) {
synchronized (sslConnector) {
echoBytesProcessed.set(0);
CountDownLatch responseArrivedLatch = new CountDownLatch(1);
responseArrivedLatchHolder.set(responseArrivedLatch);
readBB.clear();
writeBB.position(testData.length - 3);
byte[] packetNum = Integer.toString(j).getBytes();
writeBB.put(packetNum);
writeBB.position(0);
writeBB.limit(testData.length);
final Callable<Object>[] callables = new Callable[SIMULT_CLIENTS];
for (int x = 0; x < SIMULT_CLIENTS; x++) {
callables[x] = new Callable() {
public Object call() throws Exception {
ByteBuffer bb = writeBB.duplicate();
sslConnector.writeToAsyncQueue(bb);
return null;
}
};
}
ExecutorService executor = Executors.newFixedThreadPool(SIMULT_THREADS_COUNT);
List<Callable<Object>> c = Arrays.asList(callables);
try {
executor.invokeAll(c);
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
while (sslConnector.read(readBB, false) > 0 && readBB.position() < testData.length * SIMULT_CLIENTS);
}
if (readBB.position() < testData.length * SIMULT_CLIENTS) {
waitOnLatch(responseArrivedLatchHolder.get(), 30, TimeUnit.SECONDS);
}
synchronized(sslConnector) {
readBB.flip();
String val1 = new String(testData);
StringBuffer patternBuffer = new StringBuffer();
for (int x = 0; x < SIMULT_CLIENTS; x++) {
patternBuffer.append(val1);
}
val1 = patternBuffer.toString();
String val2 = new String(toArray(readBB));
if (!val1.equals(val2)) {
Controller.logger().log(Level.INFO, "Client#" + i + " Packet#" + j);
Controller.logger().log(Level.INFO, "EXPECTED SIZE: " + testData.length * SIMULT_CLIENTS);
Controller.logger().log(Level.INFO, "VAL1: " + val1);
Controller.logger().log(Level.INFO, "VAL2: " + val2);
Controller.logger().log(Level.INFO, "READBB: " + readBB);
Controller.logger().log(Level.INFO, "LATCH: " + responseArrivedLatchHolder.get().getCount());
Controller.logger().log(Level.INFO, "QUEUE HAS ELEMENTS? : " + sslConnector.getSelectorHandler().getAsyncQueueWriter().isReady(sslConnector.getUnderlyingChannel().keyFor(sslConnector.getSelectorHandler().getSelector())));
Controller.logger().log(Level.INFO, "EchoFilter processed bytes: " + echoBytesProcessed.get());
}
assertEquals(val1, val2);
}
}
} finally {
sslConnector.close();
controller.releaseConnectorHandler(sslConnector);
}
}
} finally {
try{
controller.stop();
} catch (Throwable t){
t.printStackTrace();
}
Controller.logger().log(Level.INFO, "Finished SSLAsyncQueueWriterTest.testSeveralPackets() test");
}
}
private Controller createSSLController(SSLContext sslContext) {
final SSLReadFilter readFilter = new SSLReadFilter();
readFilter.setSSLContext(sslContext);
final SSLEchoAsyncWriteQueueFilter echoFilter = new SSLEchoAsyncWriteQueueFilter() {
@Override
public boolean execute(Context ctx) throws IOException {
final WorkerThread workerThread = ((WorkerThread)Thread.currentThread());
echoBytesProcessed.addAndGet(workerThread.getByteBuffer().position());
return super.execute(ctx);
}
};
SSLSelectorHandler selectorHandler = new SSLSelectorHandler();
selectorHandler.setPort(PORT);
final Controller controller = new Controller();
controller.setSelectorHandler(selectorHandler);
controller.setHandleReadWriteConcurrently(true);
controller.setProtocolChainInstanceHandler(new DefaultProtocolChainInstanceHandler() {
@Override
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 AtomicReference<CountDownLatch>(responseArrivedLatch),
new AtomicReference<CountDownLatch>(handshakeDoneLatch),
writeBB, readBB);
}
private SSLCallbackHandler createCallbackHandler(final Controller controller,
final SSLConnectorHandler sslConnector,
final AtomicReference<CountDownLatch> responseArrivedLatchHolder,
final AtomicReference<CountDownLatch> handshakeDoneLatchHolder,
final ByteBuffer writeBB, final ByteBuffer readBB) {
return new SSLCallbackHandler<Context>() {
public void onConnect(IOEvent<Context> ioEvent) {
synchronized (sslConnector) {
SelectionKey key = ioEvent.attachment().getSelectionKey();
try {
sslConnector.finishConnect(key);
} catch (IOException ex) {
ex.printStackTrace();
return;
}
}
}
public void onRead(IOEvent<Context> ioEvent) {
synchronized(sslConnector) {
try {
while (sslConnector.read(readBB, false) > 0 &&
readBB.position() < writeBB.capacity() * SIMULT_CLIENTS);
if (readBB.position() >= writeBB.limit() * SIMULT_CLIENTS) {
responseArrivedLatchHolder.get().countDown();
}
} catch (IOException ex) {
ex.printStackTrace();
sslConnector.getSelectorHandler().getSelectionKeyHandler().
cancel(ioEvent.attachment().getSelectionKey());
}
}
}
public void onWrite(IOEvent<Context> ioEvent) {
}
public void onHandshake(IOEvent<Context> ioEvent) {
synchronized(sslConnector) {
readBB.clear();
ioEvent.attachment().getSelectionKey().interestOps(0);
handshakeDoneLatchHolder.get().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;
}
}