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

org.eclipse.jetty.server.MemoryConnector 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.server;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;

import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.MemoryEndPointPipe;
import org.eclipse.jetty.util.thread.ExecutionStrategy;
import org.eclipse.jetty.util.thread.Invocable;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

A server {@link Connector} that allows clients to communicate via memory.

*

Typical usage on the server-side:

*
{@code
 * Server server = new Server();
 * MemoryConnector memoryConnector = new MemoryConnector(server, new HttpConnectionFactory());
 * server.addConnector(memoryConnector);
 * server.start();
 * }
*

Typical usage on the client-side:

*
 {@code
 * // Connect to the server and get the local, client-side, EndPoint.
 * EndPoint clientEndPoint = memoryConnector.connect().getLocalEndPoint();
 *
 * // Be ready to read responses.
 * Callback readCallback = ...;
 * clientEndPoint.fillInterested(readCallback);
 *
 * // Write a request to the server.
 * ByteBuffer request = StandardCharsets.UTF_8.encode("""
 *     GET / HTTP/1.1
 *     Host: localhost
 *
 *     """);
 * Callback.Completable writeCallback = new Callback.Completable();
 * clientEndPoint.write(writeCallback, request);
 * }
*/ public class MemoryConnector extends AbstractConnector { private static final Logger LOG = LoggerFactory.getLogger(MemoryConnector.class); private final SocketAddress socketAddress = new MemorySocketAddress(); private final TaskProducer producer = new TaskProducer(); private ExecutionStrategy strategy; public MemoryConnector(Server server, ConnectionFactory... factories) { this(server, null, null, null, factories); } public MemoryConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, ConnectionFactory... factories) { super(server, executor, scheduler, bufferPool, 0, factories); } @Override protected void doStart() throws Exception { strategy = new AdaptiveExecutionStrategy(producer, getExecutor()); addBean(strategy); super.doStart(); } @Override protected void doStop() throws Exception { super.doStop(); removeBean(strategy); } @Override public Object getTransport() { return null; } @Override protected void accept(int acceptorID) throws IOException, InterruptedException { // Nothing to do here. } /** *

Client-side applications use this method to connect to the server and obtain a {@link EndPoint.Pipe}.

*

Client-side applications should then use {@link EndPoint.Pipe#getLocalEndPoint()} to access the * client-side {@link EndPoint} to write requests bytes to the server and read response bytes.

* * @return a {@link EndPoint.Pipe} representing the connection between client and server */ public EndPoint.Pipe connect() { MemoryEndPointPipe pipe = new MemoryEndPointPipe(getScheduler(), producer::offer, socketAddress); accept(pipe.getRemoteEndPoint()); if (LOG.isDebugEnabled()) LOG.debug("connected {} to {}", pipe, this); return pipe; } private void accept(EndPoint endPoint) { endPoint.setIdleTimeout(getIdleTimeout()); AbstractConnection connection = (AbstractConnection)getDefaultConnectionFactory().newConnection(this, endPoint); endPoint.setConnection(connection); endPoint.onOpen(); onEndPointOpened(endPoint); connection.addEventListener(new Connection.Listener() { @Override public void onClosed(Connection connection) { onEndPointClosed(endPoint); } }); connection.onOpen(); if (LOG.isDebugEnabled()) LOG.debug("accepted {} in {}", endPoint, this); } /** * @return the local {@link SocketAddress} of this connector */ public SocketAddress getLocalSocketAddress() { return socketAddress; } private class TaskProducer implements ExecutionStrategy.Producer { private final Queue tasks = new ConcurrentLinkedQueue<>(); @Override public Runnable produce() { return tasks.poll(); } private void offer(Invocable.Task task) { if (LOG.isDebugEnabled()) LOG.debug("offer {} to {}", task, MemoryConnector.this); tasks.offer(task); strategy.produce(); } } private class MemorySocketAddress extends SocketAddress { private final String address = "[memory:@%x]".formatted(System.identityHashCode(MemoryConnector.this)); @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof MemorySocketAddress that) return address.equals(that.address); return false; } @Override public int hashCode() { return address.hashCode(); } @Override public String toString() { return address; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy