com.pdd.pop.ext.glassfish.grizzly.streams.AbstractStreamReader Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2011 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.streams;
import java.io.EOFException;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.pdd.pop.ext.glassfish.grizzly.Buffer;
import com.pdd.pop.ext.glassfish.grizzly.CompletionHandler;
import com.pdd.pop.ext.glassfish.grizzly.Connection;
import com.pdd.pop.ext.glassfish.grizzly.Grizzly;
import com.pdd.pop.ext.glassfish.grizzly.GrizzlyFuture;
import com.pdd.pop.ext.glassfish.grizzly.Transformer;
import com.pdd.pop.ext.glassfish.grizzly.impl.FutureImpl;
import com.pdd.pop.ext.glassfish.grizzly.impl.ReadyFutureImpl;
import com.pdd.pop.ext.glassfish.grizzly.impl.SafeFutureImpl;
import com.pdd.pop.ext.glassfish.grizzly.utils.CompletionHandlerAdapter;
import com.pdd.pop.ext.glassfish.grizzly.utils.ResultAware;
import com.pdd.pop.ext.glassfish.grizzly.utils.conditions.Condition;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Each method reads data from the current ByteBuffer. If not enough data
* is present in the current ByteBuffer, discard is called on the current
* ByteBuffer and we advance to the next ByteBuffer, or block if not enough
* data is present. If close() is called, all subsequent method calls will
* throw an IllegalStateException, and any threads blocked waiting for more
* data will be unblocked, and continue with an IllegalStateException from the
* blocking method call.
*
* dataReceived and close may be safely invoked by multiple threads.
* The other methods must be invoked only by one thread, which is the reader of
* this data stream.
*
* @author Ken Cavanaugh
* @author Alexey Stashok
*/
public abstract class AbstractStreamReader implements StreamReader {
private static final boolean DEBUG = false;
private static final Logger LOGGER = Grizzly.logger(AbstractStreamReader.class);
protected final Connection connection;
protected final Input input;
protected final AtomicBoolean isClosed = new AtomicBoolean(false);
private static void msg(final String msg) {
LOGGER.log(Level.INFO, "READERSTREAM:DEBUG:{0}", msg);
}
private static void displayBuffer(final String str,
final Buffer wrapper) {
msg(str);
msg("\tposition() = " + wrapper.position());
msg("\tlimit() = " + wrapper.limit());
msg("\tcapacity() = " + wrapper.capacity());
}
// Concurrency considerations:
// Only one thread (the consumer) may invoke the readXXX methods.
// dataReceived and close may be invoked by a producer thread.
// The consumer thread will invoke readXXX methods far more often
// than a typical producer will call dataReceived or (possibly) close.
// So buffers must be protected from concurrent access, either by locking
// or by a wait-free queue. However, volatile is sufficient for current,
// since we just need to ensure the visibility of the value of current to
// all threads.
//
/**
* Create a new ByteBufferReader.
*
* @param connection the {@link Connection} to be associated with this
* AbstractStreamReader
* @param streamInput the stream source
*/
protected AbstractStreamReader(Connection connection, Input streamInput) {
this.input = streamInput;
this.connection = connection;
}
/**
* {@inheritDoc}
*/
@Override
public boolean readBoolean() throws IOException {
return readByte() == 1;
}
/**
* {@inheritDoc}
*/
@Override
public byte readByte() throws IOException {
return input.read();
}
/**
* {@inheritDoc}
*/
@Override
public char readChar() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 2) {
final char result = buffer.getChar();
buffer.shrink();
return result;
}
}
return (char) ((readByte() & 0xff) << 8 | readByte() & 0xff);
}
/**
* {@inheritDoc}
*/
@Override
public short readShort() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 2) {
final short result = buffer.getShort();
buffer.shrink();
return result;
}
}
return (short) ((readByte() & 0xff) << 8 | readByte() & 0xff);
}
/**
* {@inheritDoc}
*/
@Override
public int readInt() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 4) {
final int result = buffer.getInt();
buffer.shrink();
return result;
}
}
return (readShort() & 0xffff) << 16 | readShort() & 0xffff;
}
/**
* {@inheritDoc}
*/
@Override
public long readLong() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 8) {
final long result = buffer.getLong();
buffer.shrink();
return result;
}
}
return (readInt() & 0xffffffffL) << 32 | readInt() & 0xffffffffL;
}
/**
* {@inheritDoc}
*/
@Override
final public float readFloat() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 4) {
final float result = buffer.getFloat();
buffer.shrink();
return result;
}
}
return Float.intBitsToFloat(readInt());
}
/**
* {@inheritDoc}
*/
@Override
final public double readDouble() throws IOException {
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
if (buffer != null && buffer.remaining() >= 8) {
final double result = buffer.getDouble();
buffer.shrink();
return result;
}
}
return Double.longBitsToDouble(readLong());
}
private void arraySizeCheck(final int sizeInBytes) {
if (sizeInBytes > available()) {
throw new BufferUnderflowException();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readBooleanArray(boolean[] data) throws IOException {
arraySizeCheck(data.length);
for (int ctr = 0; ctr < data.length; ctr++) {
data[ctr] = readBoolean();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readByteArray(final byte[] data) throws IOException {
readByteArray(data, 0, data.length);
}
/**
* {@inheritDoc}
*/
@Override
public void readByteArray(byte[] data, int offset, int length) throws IOException {
arraySizeCheck(length);
if (input.isBuffered()) {
final Buffer buffer = input.getBuffer();
buffer.get(data, offset, length);
buffer.shrink();
} else {
for(int i = offset; i < length; i++) {
data[i] = input.read();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void readBytes(final Buffer buffer) throws IOException {
if (!buffer.hasRemaining()) {
return;
}
arraySizeCheck(buffer.remaining());
if (input.isBuffered()) {
final Buffer inputBuffer = input.getBuffer();
final int diff = buffer.remaining() - inputBuffer.remaining();
if (diff >= 0) {
buffer.put(inputBuffer);
} else {
final int save = inputBuffer.limit();
inputBuffer.limit(save + diff);
buffer.put(inputBuffer);
inputBuffer.limit(save);
}
inputBuffer.shrink();
} else {
while(buffer.hasRemaining()) {
buffer.put(input.read());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void readCharArray(final char[] data) throws IOException {
arraySizeCheck(2 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readChar();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readShortArray(final short[] data) throws IOException {
arraySizeCheck(2 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readShort();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readIntArray(final int[] data) throws IOException {
arraySizeCheck(4 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readInt();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readLongArray(final long[] data) throws IOException {
arraySizeCheck(8 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readLong();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readFloatArray(final float[] data) throws IOException {
arraySizeCheck(4 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readFloat();
}
}
/**
* {@inheritDoc}
*/
@Override
public void readDoubleArray(final double[] data) throws IOException {
arraySizeCheck(8 * data.length);
for (int i = 0; i < data.length; i++) {
data[i] = readDouble();
}
}
@Override
public void skip(int length) {
input.skip(length);
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyFuture decode(Transformer decoder) {
return decode(decoder, null);
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyFuture decode(Transformer decoder, CompletionHandler completionHandler) {
final FutureImpl future = SafeFutureImpl.create();
final DecodeCompletionHandler completionHandlerWrapper =
new DecodeCompletionHandler(future, completionHandler);
notifyCondition(
new StreamDecodeCondition(this, decoder, completionHandlerWrapper),
completionHandlerWrapper);
return future;
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyFuture notifyAvailable(int size) {
return notifyAvailable(size, null);
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyFuture notifyAvailable(final int size,
CompletionHandler completionHandler) {
return notifyCondition(new Condition() {
@Override
public boolean check() {
return available() >= size;
}
}, completionHandler);
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyFuture notifyCondition(Condition condition) {
return notifyCondition(condition, null);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized GrizzlyFuture notifyCondition(
final Condition condition,
final CompletionHandler completionHandler) {
if (isClosed()) {
EOFException exception = new EOFException();
if (completionHandler != null) {
completionHandler.failed(exception);
}
return ReadyFutureImpl.create(exception);
}
return input.notifyCondition(condition, completionHandler);
}
/**
* Closes the StreamReader and causes all subsequent method calls
* on this object to throw IllegalStateException.
*/
@Override
public void close() {
if (isClosed.compareAndSet(false, true)) {
if (input != null) {
try {
input.close();
} catch (IOException ignored) {
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isClosed() {
return isClosed.get();
}
/**
* {@inheritDoc}
*/
@Override
public final boolean hasAvailable() {
return available() > 0;
}
/**
* {@inheritDoc}
*/
@Override
public int available() {
return input.size();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSupportBufferWindow() {
return input.isBuffered();
}
/**
* {@inheritDoc}
*/
@Override
public Buffer getBufferWindow() {
return input.getBuffer();
}
/**
* {@inheritDoc}
*/
@Override
public Buffer takeBufferWindow() {
return input.takeBuffer();
}
/**
* {@inheritDoc}
*/
@Override
public Connection getConnection() {
return connection;
}
private static class DecodeCompletionHandler extends CompletionHandlerAdapter
implements ResultAware {
private volatile A result;
public DecodeCompletionHandler(FutureImpl future,
CompletionHandler completionHandler) {
super(future, completionHandler);
}
@Override
public void setResult(A result) {
this.result = result;
}
@Override
protected A adapt(B result) {
return this.result;
}
}
}