com.pdd.pop.ext.glassfish.grizzly.nio.NIOConnection Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2015 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.pdd.pop.ext.glassfish.grizzly.nio;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.pdd.pop.ext.glassfish.grizzly.*;
import com.pdd.pop.ext.glassfish.grizzly.asyncqueue.AsyncReadQueueRecord;
import com.pdd.pop.ext.glassfish.grizzly.asyncqueue.AsyncWriteQueueRecord;
import com.pdd.pop.ext.glassfish.grizzly.asyncqueue.TaskQueue;
import com.pdd.pop.ext.glassfish.grizzly.attributes.AttributeHolder;
import com.pdd.pop.ext.glassfish.grizzly.impl.FutureImpl;
import com.pdd.pop.ext.glassfish.grizzly.memory.Buffers;
import com.pdd.pop.ext.glassfish.grizzly.memory.MemoryManager;
import com.pdd.pop.ext.glassfish.grizzly.monitoring.MonitoringConfig;
import com.pdd.pop.ext.glassfish.grizzly.monitoring.DefaultMonitoringConfig;
import com.pdd.pop.ext.glassfish.grizzly.utils.CompletionHandlerAdapter;
import com.pdd.pop.ext.glassfish.grizzly.utils.DataStructures;
import com.pdd.pop.ext.glassfish.grizzly.utils.Futures;
import com.pdd.pop.ext.glassfish.grizzly.utils.NullaryFunction;
/**
* Common {@link Connection} implementation for Java NIO Connections.
*
* @author Alexey Stashok
*/
public abstract class NIOConnection implements Connection {
protected static final Object NOTIFICATION_INITIALIZED = Boolean.TRUE;
protected static final Object NOTIFICATION_CLOSED_COMPLETE = Boolean.FALSE;
private static final boolean WIN32 = "\\".equals(System.getProperty("file.separator"));
private static final Logger LOGGER = Grizzly.logger(NIOConnection.class);
private static final short MAX_ZERO_READ_COUNT = 100;
/**
* Is initial OP_READ enabling required for the connection
*/
private boolean isInitialReadRequired = true;
protected final NIOTransport transport;
protected volatile int maxAsyncWriteQueueSize;
protected volatile long readTimeoutMillis = 30000;
protected volatile long writeTimeoutMillis = 30000;
protected volatile SelectableChannel channel;
protected volatile SelectionKey selectionKey;
protected volatile SelectorRunner selectorRunner;
protected volatile Processor processor;
protected volatile ProcessorSelector processorSelector;
protected final AttributeHolder attributes;
private volatile TaskQueue asyncReadQueue;
private final TaskQueue asyncWriteQueue;
// Semaphor responsible for connect/close notification
protected final AtomicReferenceFieldUpdater connectCloseSemaphorUpdater =
AtomicReferenceFieldUpdater.newUpdater(NIOConnection.class,
Object.class, "connectCloseSemaphor");
private volatile Object connectCloseSemaphor;
// closeTypeFlag, "null" value means the connection is open.
private final AtomicBoolean isCloseScheduled = new AtomicBoolean();
private final AtomicReferenceFieldUpdater closeReasonUpdater =
AtomicReferenceFieldUpdater.newUpdater(NIOConnection.class,
CloseReason.class, "closeReason");
private volatile CloseReason closeReason;
private volatile GrizzlyFuture closeFuture;
protected volatile boolean isBlocking;
protected volatile boolean isStandalone;
protected short zeroByteReadCount;
private final Queue closeListeners =
new ConcurrentLinkedQueue();
/**
* Storage contains states of different Processors this Connection is associated with.
*/
private final ProcessorStatesMap processorStateStorage =
new ProcessorStatesMap();
/**
* Connection probes
*/
protected final DefaultMonitoringConfig monitoringConfig =
new DefaultMonitoringConfig(ConnectionProbe.class);
public NIOConnection(final NIOTransport transport) {
this.transport = transport;
asyncWriteQueue = TaskQueue.createTaskQueue(
new TaskQueue.MutableMaxQueueSize() {
@Override
public int getMaxQueueSize() {
return maxAsyncWriteQueueSize;
}
});
attributes = transport.getAttributeBuilder().createSafeAttributeHolder();
}
@Override
public void configureBlocking(boolean isBlocking) {
this.isBlocking = isBlocking;
}
@Override
public boolean isBlocking() {
return isBlocking;
}
@Override
public MemoryManager> getMemoryManager() {
return transport.getMemoryManager();
}
@Override
public synchronized void configureStandalone(boolean isStandalone) {
if (this.isStandalone != isStandalone) {
this.isStandalone = isStandalone;
if (isStandalone) {
processor = StandaloneProcessor.INSTANCE;
processorSelector = StandaloneProcessorSelector.INSTANCE;
} else {
processor = transport.getProcessor();
processorSelector = transport.getProcessorSelector();
}
}
}
@Override
public boolean isStandalone() {
return isStandalone;
}
@Override
public Transport getTransport() {
return transport;
}
/**
* {@inheritDoc}
*/
@Override
public int getMaxAsyncWriteQueueSize() {
return maxAsyncWriteQueueSize;
}
/**
* {@inheritDoc}
*/
@Override
public void setMaxAsyncWriteQueueSize(int maxAsyncWriteQueueSize) {
this.maxAsyncWriteQueueSize = maxAsyncWriteQueueSize;
}
@Override
public long getReadTimeout(TimeUnit timeUnit) {
return timeUnit.convert(readTimeoutMillis, TimeUnit.MILLISECONDS);
}
@Override
public void setReadTimeout(long timeout, TimeUnit timeUnit) {
readTimeoutMillis = TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
}
@Override
public long getWriteTimeout(TimeUnit timeUnit) {
return timeUnit.convert(writeTimeoutMillis, TimeUnit.MILLISECONDS);
}
@Override
public void setWriteTimeout(long timeout, TimeUnit timeUnit) {
writeTimeoutMillis = TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
}
public SelectorRunner getSelectorRunner() {
return selectorRunner;
}
protected void setSelectorRunner(SelectorRunner selectorRunner) {
this.selectorRunner = selectorRunner;
}
public void attachToSelectorRunner(final SelectorRunner selectorRunner)
throws IOException {
detachSelectorRunner();
final SelectorHandler selectorHandler = transport.getSelectorHandler();
final FutureImpl future =
Futures.createSafeFuture();
selectorHandler.registerChannelAsync(
selectorRunner, channel, 0, this, Futures.toCompletionHandler(future));
try {
final RegisterChannelResult result =
future.get(readTimeoutMillis, TimeUnit.MILLISECONDS);
this.selectorRunner = selectorRunner;
this.selectionKey = result.getSelectionKey();
} catch (InterruptedException e) {
throw new IOException("", e);
} catch (ExecutionException e) {
throw new IOException("", e.getCause());
} catch (TimeoutException e) {
throw new IOException("", e);
}
}
public void detachSelectorRunner() throws IOException {
final SelectorRunner selectorRunnerLocal = this.selectorRunner;
this.selectionKey = null;
this.selectorRunner = null;
if (selectorRunnerLocal != null) {
transport.getSelectorHandler().deregisterChannel(selectorRunnerLocal,
channel);
}
}
public SelectableChannel getChannel() {
return channel;
}
protected void setChannel(SelectableChannel channel) {
this.channel = channel;
}
public SelectionKey getSelectionKey() {
return selectionKey;
}
protected void setSelectionKey(SelectionKey selectionKey) {
this.selectionKey = selectionKey;
setChannel(selectionKey.channel());
}
@Override
public Processor obtainProcessor(IOEvent ioEvent) {
if (processor == null && processorSelector == null) {
return transport.obtainProcessor(ioEvent, this);
}
if (processor != null && processor.isInterested(ioEvent)) {
return processor;
} else if (processorSelector != null) {
final Processor selectedProcessor =
processorSelector.select(ioEvent, this);
if (selectedProcessor != null) {
return selectedProcessor;
}
}
return null;
}
@Override
public Processor getProcessor() {
return processor;
}
@Override
public void setProcessor(
Processor preferableProcessor) {
this.processor = preferableProcessor;
}
@Override
public ProcessorSelector getProcessorSelector() {
return processorSelector;
}
@Override
public void setProcessorSelector(
final ProcessorSelector preferableProcessorSelector) {
this.processorSelector = preferableProcessorSelector;
}
@Override
public E obtainProcessorState(final Processor processor,
final NullaryFunction factory) {
return processorStateStorage.getState(processor, factory);
}
@Override
public void executeInEventThread(final IOEvent event, final Runnable runnable) {
final Executor threadPool = transport.getIOStrategy()
.getThreadPoolFor(this, event);
if (threadPool == null) {
transport.getSelectorHandler().enque(selectorRunner,
new SelectorHandler.Task() {
@Override
public boolean run() throws Exception {
runnable.run();
return true;
}
}, null);
} else {
threadPool.execute(runnable);
}
}
public TaskQueue getAsyncReadQueue() {
if (asyncReadQueue == null) {
synchronized (this) {
if (asyncReadQueue == null) {
asyncReadQueue = TaskQueue.createTaskQueue(null);
}
}
}
return asyncReadQueue;
}
public TaskQueue getAsyncWriteQueue() {
return asyncWriteQueue;
}
@Override
public AttributeHolder getAttributes() {
return attributes;
}
@Override
public GrizzlyFuture> read() {
final FutureImpl> future =
Futures.createSafeFuture();
read(Futures.toCompletionHandler(future));
return future;
}
@SuppressWarnings("unchecked")
@Override
public void read(
final CompletionHandler> completionHandler) {
final Processor obtainedProcessor = obtainProcessor(IOEvent.READ);
obtainedProcessor.read(this, completionHandler);
}
@Override
public GrizzlyFuture> write(final M message) {
final FutureImpl> future =
Futures.createSafeFuture();
write(null, message, Futures.toCompletionHandler(future), null);
return future;
}
@Override
public void write(final M message,
final CompletionHandler> completionHandler) {
write(null, message, completionHandler, null);
}
@Override
@Deprecated
public void write(final M message,
CompletionHandler> completionHandler,
com.pdd.pop.ext.glassfish.grizzly.asyncqueue.PushBackHandler pushbackHandler) {
write(null, message, completionHandler, pushbackHandler);
}
@Override
public void write(final SocketAddress dstAddress, final M message,
final CompletionHandler> completionHandler) {
write(dstAddress, message, completionHandler, null);
}
@SuppressWarnings("unchecked")
@Override
@Deprecated
public void write(
final SocketAddress dstAddress, final M message,
final CompletionHandler> completionHandler,
final com.pdd.pop.ext.glassfish.grizzly.asyncqueue.PushBackHandler pushbackHandler) {
final Processor obtainedProcessor = obtainProcessor(IOEvent.WRITE);
obtainedProcessor.write(this, dstAddress, message,
completionHandler, pushbackHandler);
}
@Override
public boolean isOpen() {
return channel != null && channel.isOpen() && closeReason == null;
}
@Override
public void assertOpen() throws IOException {
final CloseReason reason = getCloseReason();
if (reason != null) {
throw new IOException("Connection is closed", reason.getCause());
}
}
public boolean isClosed() {
return !isOpen();
}
@Override
public CloseReason getCloseReason() {
final CloseReason cr = closeReason;
if (cr != null) {
return cr;
} else if (channel == null || !channel.isOpen()) {
return CloseReason.LOCALLY_CLOSED_REASON;
}
return null;
}
@Override
public void terminateSilently() {
terminate0(null, CloseReason.LOCALLY_CLOSED_REASON);
}
@Override
public GrizzlyFuture terminate() {
final FutureImpl future = Futures.createSafeFuture();
terminate0(Futures.toCompletionHandler(future),
CloseReason.LOCALLY_CLOSED_REASON);
return future;
}
@Override
public void terminateWithReason(final IOException reason) {
terminate0(null, new CloseReason(
com.pdd.pop.ext.glassfish.grizzly.CloseType.LOCALLY, reason));
}
@Override
public GrizzlyFuture close() {
final FutureImpl future = Futures.createSafeFuture();
closeGracefully0(Futures.toCompletionHandler(future),
CloseReason.LOCALLY_CLOSED_REASON);
return future;
}
/**
* {@inheritDoc}
* @deprecated please use {@link #close()} with the following {@link
* GrizzlyFuture#addCompletionHandler(com.pdd.pop.ext.glassfish.grizzly.CompletionHandler)} call
*/
@Override
public void close(final CompletionHandler completionHandler) {
closeGracefully0(completionHandler, CloseReason.LOCALLY_CLOSED_REASON);
}
@Override
@SuppressWarnings("unchecked")
public final void closeSilently() {
closeGracefully0(null, CloseReason.LOCALLY_CLOSED_REASON);
}
@Override
public void closeWithReason(final IOException reason) {
closeGracefully0(null, new CloseReason(
com.pdd.pop.ext.glassfish.grizzly.CloseType.LOCALLY, reason));
}
@Override
public GrizzlyFuture closeFuture() {
if (closeFuture == null) {
synchronized (this) {
if (closeFuture == null) {
final CloseReason cr = closeReason;
if (cr == null) {
final FutureImpl f
= Futures.createSafeFuture();
addCloseListener(new com.pdd.pop.ext.glassfish.grizzly.CloseListener() {
@Override
public void onClosed(Closeable closeable, ICloseType type)
throws IOException {
final CloseReason cr = closeReason;
assert cr != null;
f.result(cr);
}
});
closeFuture = f;
} else {
closeFuture = Futures.createReadyFuture(cr);
}
}
}
}
return closeFuture;
}
@SuppressWarnings("unchecked")
protected void closeGracefully0(
final CompletionHandler completionHandler,
CloseReason closeReason) {
if (isCloseScheduled.compareAndSet(false, true)) {
if (LOGGER.isLoggable(Level.FINEST)) {
// replace close reason: clone the original value and add stacktrace
closeReason = new CloseReason(closeReason.getType(),
new IOException("Connection is closed at",
closeReason.getCause()));
}
final CloseReason finalReason = closeReason;
transport.getWriter(this).write(this, Buffers.EMPTY_BUFFER,
new EmptyCompletionHandler>() {
@Override
public void completed(final WriteResult result) {
terminate0(completionHandler, finalReason);
}
@Override
public void failed(final Throwable throwable) {
terminate0(completionHandler, finalReason);
}
});
} else {
if (completionHandler != null) {
addCloseListener(new com.pdd.pop.ext.glassfish.grizzly.CloseListener() {
@Override
public void onClosed(final Closeable closeable,
final ICloseType type) throws IOException {
completionHandler.completed(NIOConnection.this);
}
});
}
}
}
protected void terminate0(final CompletionHandler completionHandler,
final CloseReason reason) {
isCloseScheduled.set(true);
if (closeReasonUpdater.compareAndSet(this, null, reason)) {
if (LOGGER.isLoggable(Level.FINEST)) {
// replace close reason: clone the original value and add stacktrace
this.closeReason = new CloseReason(reason.getType(),
new IOException("Connection is closed at",
reason.getCause()));
}
preClose();
notifyCloseListeners(reason);
notifyProbesClose(this);
transport.getSelectorHandler().execute(
selectorRunner, new SelectorHandler.Task() {
@Override
public boolean run() {
try {
doClose();
} catch (IOException e) {
LOGGER.log(Level.FINE, "Error during connection close", e);
}
return true;
}
}, new CompletionHandlerAdapter(
null, completionHandler) {
@Override
protected Connection adapt(final SelectorHandler.Task result) {
return NIOConnection.this;
}
@Override
public void failed(final Throwable throwable) {
try {
doClose();
} catch (Exception ignored) {
}
completed(null);
}
});
} else {
Futures.notifyResult(null, completionHandler, this);
}
}
/**
* Do the actual connection close.
*/
protected void doClose() throws IOException {
transport.closeConnection(this);
}
/**
* {@inheritDoc}
*/
@Override
public void addCloseListener(final com.pdd.pop.ext.glassfish.grizzly.CloseListener closeListener) {
CloseReason reason = closeReason;
// check if connection is still open
if (reason == null) {
// add close listener
closeListeners.add(closeListener);
// check the connection state again
reason = closeReason;
if (reason != null && closeListeners.remove(closeListener)) {
// if connection was closed during the method call - notify the listener
invokeCloseListener(closeListener, reason.getType());
}
} else { // if connection is closed - notify the listener
invokeCloseListener(closeListener, reason.getType());
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeCloseListener(final com.pdd.pop.ext.glassfish.grizzly.CloseListener closeListener) {
return closeListeners.remove(closeListener);
}
@Override
public void addCloseListener(CloseListener closeListener) {
addCloseListener((com.pdd.pop.ext.glassfish.grizzly.CloseListener) closeListener);
}
@Override
public boolean removeCloseListener(CloseListener closeListener) {
return removeCloseListener((com.pdd.pop.ext.glassfish.grizzly.CloseListener) closeListener);
}
/**
* {@inheritDoc}
*/
@Override
public void notifyConnectionError(Throwable error) {
notifyProbesError(this, error);
}
/**
* {@inheritDoc}
*/
@Override
public final MonitoringConfig getMonitoringConfig() {
return monitoringConfig;
}
/**
* Notify registered {@link ConnectionProbe}s about the bind event.
*
* @param connection the Connection event occurred on.
*/
protected static void notifyProbesBind(NIOConnection connection) {
final ConnectionProbe[] probes = connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onBindEvent(connection);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the accept event.
*
* @param serverConnection the server Connection, which accepted the client connection.
* @param clientConnection the client Connection.
*/
protected static void notifyProbesAccept(final NIOConnection serverConnection,
final NIOConnection clientConnection) {
final ConnectionProbe[] probes =
serverConnection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onAcceptEvent(serverConnection, clientConnection);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the connect event.
*
* @param connection the Connection event occurred on.
*/
protected static void notifyProbesConnect(NIOConnection connection) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onConnectEvent(connection);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the read event.
*/
protected static void notifyProbesRead(NIOConnection connection,
Buffer data, int size) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onReadEvent(connection, data, size);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the write event.
*/
protected static void notifyProbesWrite(NIOConnection connection,
Buffer data, long size) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onWriteEvent(connection, data, size);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the IO Event ready event.
*
* @param connection the Connection event occurred on.
* @param ioEvent the {@link IOEvent}.
*/
protected static void notifyIOEventReady(NIOConnection connection,
IOEvent ioEvent) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onIOEventReadyEvent(connection, ioEvent);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the IO Event enabled event.
*
* @param connection the Connection event occurred on.
* @param ioEvent the {@link IOEvent}.
*/
protected static void notifyIOEventEnabled(NIOConnection connection,
IOEvent ioEvent) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onIOEventEnableEvent(connection, ioEvent);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the IO Event disabled event.
*
* @param connection the Connection event occurred on.
* @param ioEvent the {@link IOEvent}.
*/
protected static void notifyIOEventDisabled(NIOConnection connection,
IOEvent ioEvent) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onIOEventDisableEvent(connection, ioEvent);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the close event.
*
* @param connection the Connection event occurred on.
*/
protected static void notifyProbesClose(NIOConnection connection) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onCloseEvent(connection);
}
}
}
/**
* Notify registered {@link ConnectionProbe}s about the error.
*
* @param connection the Connection event occurred on.
*/
protected static void notifyProbesError(NIOConnection connection,
Throwable error) {
final ConnectionProbe[] probes =
connection.monitoringConfig.getProbesUnsafe();
if (probes != null) {
for (ConnectionProbe probe : probes) {
probe.onErrorEvent(connection, error);
}
}
}
/**
* Notify all close listeners
*/
private void notifyCloseListeners(final CloseReason closeReason) {
final com.pdd.pop.ext.glassfish.grizzly.CloseType closeType =
closeReason.getType();
com.pdd.pop.ext.glassfish.grizzly.CloseListener closeListener;
while ((closeListener = closeListeners.poll()) != null) {
invokeCloseListener(closeListener, closeType);
}
}
protected void preClose() {
// Check if connection init event (like CONNECT or ACCEPT) has been sent
if (connectCloseSemaphorUpdater.getAndSet(this, NOTIFICATION_CLOSED_COMPLETE) ==
NOTIFICATION_INITIALIZED) {
transport.fireIOEvent(IOEvent.CLOSED, this, null);
}
}
/**
* Enables OP_READ if it has never been enabled before.
*
* @throws IOException
*/
protected void enableInitialOpRead() throws IOException {
if (isInitialReadRequired) {
// isInitialReadRequired will be disabled inside
enableIOEvent(IOEvent.READ);
}
}
@Override
public void simulateIOEvent(final IOEvent ioEvent) throws IOException {
if (!isOpen()) {
// don't simulate IOEvent for closed connection
return;
}
final SelectorHandler selectorHandler = transport.getSelectorHandler();
switch (ioEvent) {
case WRITE:
selectorHandler.enque(selectorRunner, writeSimulatorRunnable, null);
break;
case READ:
selectorHandler.enque(selectorRunner, readSimulatorRunnable, null);
break;
default:
throw new IllegalArgumentException("We support only READ and WRITE events. Got " + ioEvent);
}
}
@Override
public final void enableIOEvent(final IOEvent ioEvent) throws IOException {
final boolean isOpRead = (ioEvent == IOEvent.READ);
final int interest = ioEvent.getSelectionKeyInterest();
if (interest == 0 ||
// don't register OP_READ for a connection scheduled to be closed
(isOpRead && isCloseScheduled.get()) ||
// don't register any OP for a closed connection
closeReason != null) {
return;
}
notifyIOEventEnabled(this, ioEvent);
// if OP_READ was enabled at least once - isInitialReadRequired should be false
isInitialReadRequired = isInitialReadRequired && !isOpRead;
final SelectorHandler selectorHandler = transport.getSelectorHandler();
selectorHandler.registerKeyInterest(selectorRunner, selectionKey,
interest);
}
private final SelectorHandler.Task writeSimulatorRunnable =
new SelectorHandler.Task() {
@Override
public boolean run() throws IOException {
return transport.getIOStrategy().executeIoEvent(
NIOConnection.this, IOEvent.WRITE, false);
}
};
private final SelectorHandler.Task readSimulatorRunnable =
new SelectorHandler.Task() {
@Override
public boolean run() throws IOException {
return transport.getIOStrategy().executeIoEvent(
NIOConnection.this, IOEvent.READ, false);
}
};
@Override
public final void disableIOEvent(final IOEvent ioEvent) throws IOException {
final int interest = ioEvent.getSelectionKeyInterest();
if (interest == 0) {
return;
}
notifyIOEventDisabled(this, ioEvent);
final SelectorHandler selectorHandler = transport.getSelectorHandler();
selectorHandler.deregisterKeyInterest(selectorRunner, selectionKey, interest);
}
protected final void checkEmptyRead(final int size) {
if (WIN32) {
if (size == 0) {
final short count = ++zeroByteReadCount;
if (count >= MAX_ZERO_READ_COUNT) {
closeSilently();
}
} else {
zeroByteReadCount = 0;
}
}
}
final void onSelectionKeyUpdated(final SelectionKey newSelectionKey) {
this.selectionKey = newSelectionKey;
}
@SuppressWarnings("unchecked")
private void invokeCloseListener(
final com.pdd.pop.ext.glassfish.grizzly.CloseListener closeListener,
final com.pdd.pop.ext.glassfish.grizzly.CloseType closeType) {
try {
if (closeListener instanceof CloseListener) {
CloseType closeLocal;
if (closeType == com.pdd.pop.ext.glassfish.grizzly.CloseType.LOCALLY) {
closeLocal = CloseType.LOCALLY;
} else {
closeLocal = CloseType.REMOTELY;
}
((CloseListener) closeListener).onClosed(this, closeLocal);
} else {
closeListener.onClosed(this, closeType);
}
} catch (Exception ignored) {
}
}
/**
* Set the monitoringProbes array directly.
* @param monitoringProbes
*/
void setMonitoringProbes(final ConnectionProbe[] monitoringProbes) {
this.monitoringConfig.addProbes(monitoringProbes);
}
/**
* Map, which contains {@link Processor}s and their states related to this {@link Connection}.
*/
private final static class ProcessorStatesMap {
private volatile int volatileFlag;
private ProcessorState singleProcessorState;
private ConcurrentMap processorStatesMap;
@SuppressWarnings("unchecked")
public E getState(final Processor processor,
final NullaryFunction stateFactory) {
final int c = volatileFlag;
if (c == 0) {
// Connection doesn't have any processor state associated
return (E) getStateSync(processor, stateFactory);
} else {
final ProcessorState localProcessorState = singleProcessorState;
if (localProcessorState != null) {
if (localProcessorState.processor.equals(processor)) {
// the normal code path (Connection has only one processor associated)
return (E) localProcessorState.state;
}
} else {
return (E) getStateSync(processor, stateFactory);
}
// Code should be invoked in PU cases only, so move it under
// static class to let Hotspot initialize it lazily only when
// needed
return (E) StaticMapAccessor.getFromMap(this, processor, stateFactory);
}
}
private synchronized Object getStateSync(final Processor processor,
final NullaryFunction stateFactory) {
if (volatileFlag == 0) {
final E state = stateFactory.evaluate();
singleProcessorState = new ProcessorState(processor, state);
volatileFlag++;
return state;
} else if (volatileFlag == 1) {
if (singleProcessorState.processor.equals(processor)) {
return singleProcessorState.state;
}
}
// Code should be invoked in PU cases only, so move it under
// static class to let Hotspot initialize it lazily only when
// needed
return StaticMapAccessor.getFromMapSync(this, processor, stateFactory);
}
private static final class ProcessorState {
private final Processor processor;
private final Object state;
public ProcessorState(Processor processor, Object state) {
this.processor = processor;
this.state = state;
}
}
private static final class StaticMapAccessor {
static {
Grizzly.logger(StaticMapAccessor.class).fine("Map is going to "
+ "be used as Connection<->ProcessorState storage");
}
private static Object getFromMap(
final ProcessorStatesMap storage,
final Processor processor,
final NullaryFunction stateFactory) {
final Map localStateMap = storage.processorStatesMap;
if (localStateMap != null) {
final Object state = storage.processorStatesMap.get(processor);
if (state != null) {
return state;
}
}
return storage.getStateSync(processor, stateFactory);
}
private static Object getFromMapSync(
final ProcessorStatesMap storage,
final Processor processor,
final NullaryFunction stateFactory) {
ConcurrentMap localStatesMap =
storage.processorStatesMap;
if (localStatesMap != null) {
if (localStatesMap.containsKey(processor)) {
return localStatesMap.get(processor);
}
final Object state = stateFactory.evaluate();
localStatesMap.put(processor, state);
return state;
}
localStatesMap = DataStructures.getConcurrentMap(4);
final Object state = stateFactory.evaluate();
localStatesMap.put(processor, state);
storage.processorStatesMap = localStatesMap;
storage.volatileFlag++;
return state;
}
}
}
}