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

org.eclipse.jetty.io.MemoryEndPointPipe Maven / Gradle / Ivy

There is a newer version: 12.1.0.alpha0
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.io;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HexFormat;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.thread.AutoLock;
import org.eclipse.jetty.util.thread.Invocable;
import org.eclipse.jetty.util.thread.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

Memory-based implementation of {@link EndPoint.Pipe}.

*/ public class MemoryEndPointPipe implements EndPoint.Pipe { private static final Logger LOG = LoggerFactory.getLogger(MemoryEndPointPipe.class); private final LocalEndPoint localEndPoint; private final RemoteEndPoint remoteEndPoint; private final Consumer taskConsumer; public MemoryEndPointPipe(Scheduler scheduler, Consumer consumer, SocketAddress socketAddress) { localEndPoint = new LocalEndPoint(scheduler, socketAddress); remoteEndPoint = new RemoteEndPoint(scheduler, new MemorySocketAddress()); localEndPoint.setPeerEndPoint(remoteEndPoint); remoteEndPoint.setPeerEndPoint(localEndPoint); taskConsumer = consumer; } @Override public EndPoint getLocalEndPoint() { return localEndPoint; } @Override public EndPoint getRemoteEndPoint() { return remoteEndPoint; } private class MemoryEndPoint extends AbstractEndPoint { private static final ByteBuffer EOF = ByteBuffer.allocate(0); private final AutoLock lock = new AutoLock(); private final Deque byteBuffers = new ArrayDeque<>(); private final SocketAddress localAddress; private MemoryEndPoint peerEndPoint; private Invocable.Task fillableTask; private Invocable.Task completeWriteTask; private long maxCapacity; private long capacity; private MemoryEndPoint(Scheduler scheduler, SocketAddress localAddress) { super(scheduler); this.localAddress = localAddress; } void setPeerEndPoint(MemoryEndPoint peerEndPoint) { this.peerEndPoint = peerEndPoint; this.fillableTask = new FillableTask(peerEndPoint.getFillInterest()); this.completeWriteTask = new CompleteWriteTask(peerEndPoint.getWriteFlusher()); } public long getMaxCapacity() { return maxCapacity; } public void setMaxCapacity(long maxCapacity) { this.maxCapacity = maxCapacity; } @Override public Object getTransport() { return null; } @Override public SocketAddress getLocalSocketAddress() { return localAddress; } @Override public SocketAddress getRemoteSocketAddress() { return peerEndPoint.getLocalSocketAddress(); } @Override protected void onIncompleteFlush() { } @Override protected void needsFillInterest() { } @Override public int fill(ByteBuffer buffer) throws IOException { if (!isOpen()) throw new IOException("closed"); if (isInputShutdown()) return -1; int filled; ByteBuffer data; try (AutoLock ignored = peerEndPoint.lock.lock()) { Queue byteBuffers = peerEndPoint.byteBuffers; data = byteBuffers.peek(); if (data == null) { filled = 0; } else if (data == EOF) { filled = -1; } else { int length = data.remaining(); int space = BufferUtil.space(buffer); if (length <= space) byteBuffers.poll(); filled = Math.min(length, space); peerEndPoint.capacity -= filled; } } if (LOG.isDebugEnabled()) LOG.debug("filled {} from {}", filled, this); if (data == null) return 0; if (data == EOF) { shutdownInput(); return -1; } int copied = BufferUtil.append(buffer, data); assert copied == filled; if (filled > 0) { notIdle(); onFilled(); } return filled; } private void onFilled() { taskConsumer.accept(completeWriteTask); } @Override public boolean flush(ByteBuffer... buffers) throws IOException { if (!isOpen()) throw new IOException("closed"); if (isOutputShutdown()) throw new IOException("shutdown"); long flushed = 0; boolean result = true; try (AutoLock ignored = lock.lock()) { for (ByteBuffer buffer : buffers) { int remaining = buffer.remaining(); if (remaining == 0) continue; long newCapacity = capacity + remaining; long maxCapacity = getMaxCapacity(); if (maxCapacity > 0 && newCapacity > maxCapacity) { result = false; break; } byteBuffers.offer(BufferUtil.copy(buffer)); buffer.position(buffer.limit()); capacity = newCapacity; flushed += remaining; } } if (LOG.isDebugEnabled()) LOG.debug("flushed {} to {}", flushed, this); if (flushed > 0) { notIdle(); onFlushed(); } return result; } @Override protected void doShutdownOutput() { super.doShutdownOutput(); try (AutoLock ignored = lock.lock()) { byteBuffers.offer(EOF); } onFlushed(); } @Override protected void doClose() { super.doClose(); try (AutoLock ignored = lock.lock()) { ByteBuffer last = byteBuffers.peekLast(); if (last != EOF) byteBuffers.offer(EOF); } onFlushed(); } private void onFlushed() { taskConsumer.accept(fillableTask); } } private class LocalEndPoint extends MemoryEndPoint { private LocalEndPoint(Scheduler scheduler, SocketAddress socketAddress) { super(scheduler, socketAddress); } } private class RemoteEndPoint extends MemoryEndPoint { private RemoteEndPoint(Scheduler scheduler, SocketAddress socketAddress) { super(scheduler, socketAddress); } } private record FillableTask(FillInterest fillInterest) implements Invocable.Task { @Override public void run() { fillInterest.fillable(); } @Override public InvocationType getInvocationType() { return fillInterest.getCallbackInvocationType(); } } private record CompleteWriteTask(WriteFlusher writeFlusher) implements Invocable.Task { @Override public void run() { writeFlusher.completeWrite(); } @Override public InvocationType getInvocationType() { return writeFlusher.getCallbackInvocationType(); } } private static class MemorySocketAddress extends SocketAddress { private static final AtomicLong ID = new AtomicLong(); private final long id = ID.incrementAndGet(); private final String address = "[memory:/%s]".formatted(HexFormat.of().formatHex(ByteBuffer.allocate(8).putLong(id).array())); @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof MemorySocketAddress that) return id == that.id; return false; } @Override public int hashCode() { return Objects.hash(id); } @Override public String toString() { return address; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy