All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.common.http.DataChunk Maven / Gradle / Ivy

There is a newer version: 4.0.0-M1
Show newest version
/*
 * Copyright (c) 2018, 2021 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.common.http;

import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * The DataChunk represents a part of the HTTP body content.
 * 

* The DataChunk and the content it carries stay immutable as long as method * {@link #release()} is not called. After that, the given instance and the associated * data structure instances (e.g., the {@link ByteBuffer} array obtained by {@link #data()}) * should not be used. The idea behind this class is to be able to * minimize data copying; ideally, in order to achieve the best performance, * to not copy them at all. However, the implementations may choose otherwise. *

* The instances of this class are expected to be accessed by a single thread. Calling * the methods of this class (such as {@link #data()}, {@link #release()} from different * threads may result in a race condition unless an external synchronization is used. */ @FunctionalInterface public interface DataChunk extends Iterable { /** * Creates a simple {@link ByteBuffer} backed data chunk. The resulting * instance doesn't have any kind of a lifecycle and as such, it doesn't need * to be released. * * @param byteBuffer a byte buffer to create the request chunk from * @return a data chunk */ static DataChunk create(ByteBuffer byteBuffer) { return create(false, byteBuffer); } /** * Creates a simple byte array backed data chunk. The resulting * instance doesn't have any kind of a lifecycle and as such, it doesn't need * to be released. * * @param bytes a byte array to create the request chunk from * @return a data chunk */ static DataChunk create(byte[] bytes) { return create(false, false, ByteBuffer.wrap(bytes)); } /** * Creates a data chunk backed by one or more ByteBuffer. The resulting * instance doesn't have any kind of a lifecycle and as such, it doesn't need * to be released. * * @param byteBuffers the data for the chunk * @return a data chunk */ static DataChunk create(ByteBuffer... byteBuffers) { return new ByteBufferDataChunk(false, false, byteBuffers); } /** * Creates a reusable data chunk. * * @param flush a signal that this chunk should be written and flushed from any cache if possible * @param byteBuffers the data for this chunk. Should not be reused until {@code releaseCallback} is used * @return a reusable data chunk with no release callback */ static DataChunk create(boolean flush, ByteBuffer... byteBuffers) { return new ByteBufferDataChunk(flush, false, byteBuffers); } /** * Creates a reusable data chunk. * * @param flush a signal that this chunk should be written and flushed from any cache if possible * @param readOnly indicates underlying buffers are not reused * @param byteBuffers the data for this chunk. Should not be reused until {@code releaseCallback} is used * @return a reusable data chunk with no release callback */ static DataChunk create(boolean flush, boolean readOnly, ByteBuffer... byteBuffers) { return new ByteBufferDataChunk(flush, readOnly, byteBuffers); } /** * Creates a reusable byteBuffers chunk. * * @param flush a signal that this chunk should be written and flushed from any cache if possible * @param releaseCallback a callback which is called when this chunk is completely processed and instance is free for reuse * @param byteBuffers the data for this chunk. Should not be reused until {@code releaseCallback} is used * @return a reusable data chunk with a release callback */ static DataChunk create(boolean flush, Runnable releaseCallback, ByteBuffer... byteBuffers) { return new ByteBufferDataChunk(flush, false, releaseCallback, byteBuffers); } /** * Creates a reusable byteBuffers chunk. * * @param flush a signal that this chunk should be written and flushed from any cache if possible * @param readOnly indicates underlying buffers are not reused * @param byteBuffers the data for this chunk. Should not be reused until {@code releaseCallback} is used * @param releaseCallback a callback which is called when this chunk is completely processed and instance is free for reuse * @return a reusable data chunk with a release callback */ static DataChunk create(boolean flush, boolean readOnly, Runnable releaseCallback, ByteBuffer... byteBuffers) { return new ByteBufferDataChunk(flush, readOnly, releaseCallback, byteBuffers); } /** * Returns a representation of this chunk as an array of ByteBuffer. *

* It is expected the returned byte buffers hold references to data that * will become stale upon calling method {@link #release()}. (For instance, * the memory segment is pooled by the underlying TCP server and is reused * for a subsequent request chunk.) The idea behind this class is to be able to * minimize data copying; ideally, in order to achieve the best performance, * to not copy them at all. However, the implementations may choose otherwise. *

* Note that the methods of this instance are expected to be called by a single * thread; if not, external synchronization must be used. * * @return an array of ByteBuffer representing the data of this chunk that are guarantied to stay * immutable as long as method {@link #release()} is not called */ ByteBuffer[] data(); /** * Returns a representation of this chunk as an array of T's. * * @param clazz class of return type * @param the buffer type * @return an array of T's */ @SuppressWarnings("unchecked") default T[] data(Class clazz) { if (ByteBuffer.class.isAssignableFrom(clazz)) { return (T[]) data(); } throw new UnsupportedOperationException("Unsupported operation for class " + clazz); } /** * Checks if this instance is backed by buffers of a certain kind. * * @param clazz a buffer class instance * @param the buffer type * @return outcome of test */ default boolean isBackedBy(Class clazz) { return ByteBuffer.class.isAssignableFrom(clazz); } /** * Returns the sum of elements between the current position and the limit of each of the underlying ByteBuffer. * * @return The number of elements remaining in all underlying buffers */ default int remaining() { int remaining = 0; for (ByteBuffer byteBuffer : data()) { remaining += byteBuffer.remaining(); } return remaining; } @Override default Iterator iterator() { final ByteBuffer[] byteBuffers = data(); return new Iterator() { private int index = 0; @Override public boolean hasNext() { return index < byteBuffers.length; } @Override public ByteBuffer next() { if (index < byteBuffers.length) { return byteBuffers[index++]; } throw new NoSuchElementException(); } }; } /** * The tracing ID of this chunk. * * @return the tracing ID of this chunk */ default long id() { return System.identityHashCode(this); } /** * Gets the content of the underlying byte buffers as an array of bytes. * The returned array contains only the part of data that wasn't read yet. * Calling this method doesn't cause the underlying byte buffers to be read. *

* It is expected the returned byte array holds a reference to data that * will become stale upon calling method {@link #release()}. (For instance, * the memory segment is pooled by the underlying TCP server and is reused * for a subsequent request chunk.) The idea behind this class is to be able to * minimize data copying; ideally, in order to achieve the best performance, * to not copy them at all. However, the implementations may choose otherwise. *

* Note that the methods of this instance are expected to be called by a single * thread; if not, external synchronization must be used. * * @return an array of bytes that is guarantied to stay immutable as long as * method {@link #release()} is not called */ default byte[] bytes() { byte[] bytes = null; for (ByteBuffer byteBuffer : data()) { if (bytes == null) { bytes = Utils.toByteArray(byteBuffer.asReadOnlyBuffer()); } else { byte[] newBytes = new byte[bytes.length + byteBuffer.remaining()]; System.arraycopy(bytes, 0, newBytes, 0, bytes.length); Utils.toByteArray(byteBuffer.asReadOnlyBuffer(), newBytes, bytes.length); bytes = newBytes; } } return bytes == null ? new byte[0] : bytes; } /** * Whether this chunk is released and the associated data structures returned * by methods (such as {@link #iterator()} or {@link #bytes()}) should not be used. * The implementations may choose to not implement this optimization and to never mutate * the underlying memory; in such case this method does no-op. *

* Note that the methods of this instance are expected to be called by a single * thread; if not, external synchronization must be used. * * @return whether this chunk has been released, defaults to false */ default boolean isReleased() { return false; } /** * Releases this chunk. The underlying data as well as the data structure instances returned by * methods {@link #bytes()} and {@link #iterator()} may become stale and should not be used * anymore. The implementations may choose to not implement this optimization and to never mutate * the underlying memory; in such case this method does no-op. *

* Note that the methods of this instance are expected to be called by a single * thread; if not, external synchronization must be used. */ default void release() { } /** * Returns {@code true} if all caches are requested to flush when this chunk is written. * This method is only meaningful when handing data over to * Helidon APIs (e.g. for server response and client requests). * * @return {@code true} if it is requested to flush all caches after this chunk is written, defaults to {@code false}. */ default boolean flush() { return false; } /** * Makes a copy of this data chunk including its underlying {@link ByteBuffer}. This * may be necessary for caching in case {@link ByteBuffer#rewind()} is called to * reuse a byte buffer. Note that only the actual bytes used in the data chunk are * copied, the resulting data chunk's capacity may be less than the original. * * @return A copy of this data chunk. */ default DataChunk duplicate() { ByteBuffer[] byteBuffers = data(); ByteBuffer[] byteBuffersCopy = new ByteBuffer[byteBuffers.length]; for (int i = 0; i < byteBuffers.length; i++) { byte[] bytes = new byte[byteBuffers[i].limit()]; byteBuffers[i].get(bytes); byteBuffers[i].position(0); byteBuffersCopy[i] = ByteBuffer.wrap(bytes); } return DataChunk.create(byteBuffersCopy); } /** * Returns {@code true} if the underlying byte buffer of this chunk is read * only or {@code false} otherwise. * * @return Immutability outcome. */ default boolean isReadOnly() { return false; } /** * An empty data chunk with a flush flag can be used to force a connection * flush without actually writing any bytes. This method determines if * this chunk is used for that purpose. * * @return Outcome of test. */ default boolean isFlushChunk() { return flush() && remaining() == 0; } /** * Set a write future that will complete when data chunk has been * written to a connection. * * @param writeFuture Write future. */ default void writeFuture(CompletableFuture writeFuture) { } /** * Returns a write future associated with this chunk. * * @return Write future if one has ben set. */ default Optional> writeFuture() { return Optional.empty(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy