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

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

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
// 
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// 
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// 
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// 
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
// 
package org.eclipse.jetty.io;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;

// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint {

    private static final Logger LOG = Log.getLogger(AbstractEndPoint.class);

    private final AtomicReference _state = new AtomicReference<>(State.OPEN);

    private final long _created = System.currentTimeMillis();

    private volatile Connection _connection;

    private final FillInterest _fillInterest = new FillInterest() {

        @Override
        protected void needsFillInterest() throws IOException {
            AbstractEndPoint.this.needsFillInterest();
        }
    };

    private final WriteFlusher _writeFlusher = new WriteFlusher(this) {

        @Override
        protected void onIncompleteFlush() {
            AbstractEndPoint.this.onIncompleteFlush();
        }
    };

    protected AbstractEndPoint(Scheduler scheduler) {
        super(scheduler);
    }

    protected final void shutdownInput() {
        if (LOG.isDebugEnabled())
            LOG.debug("shutdownInput {}", this);
        while (true) {
            State s = _state.get();
            switch(s) {
                case OPEN:
                    if (!_state.compareAndSet(s, State.ISHUTTING))
                        continue;
                    try {
                        doShutdownInput();
                    } finally {
                        if (!_state.compareAndSet(State.ISHUTTING, State.ISHUT)) {
                            // If somebody else switched to CLOSED while we were ishutting,
                            // then we do the close for them
                            if (_state.get() == State.CLOSED)
                                doOnClose(null);
                            else
                                throw new IllegalStateException();
                        }
                    }
                    return;
                // Somebody else ishutting
                case ISHUTTING:
                case // Already ishut
                ISHUT:
                    return;
                case OSHUTTING:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    // The thread doing the OSHUT will close
                    return;
                case OSHUT:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    // Already OSHUT so we close
                    doOnClose(null);
                    return;
                case // already closed
                CLOSED:
                    return;
            }
        }
    }

    @Override
    public final void shutdownOutput() {
        if (LOG.isDebugEnabled())
            LOG.debug("shutdownOutput {}", this);
        while (true) {
            State s = _state.get();
            switch(s) {
                case OPEN:
                    if (!_state.compareAndSet(s, State.OSHUTTING))
                        continue;
                    try {
                        doShutdownOutput();
                    } finally {
                        if (!_state.compareAndSet(State.OSHUTTING, State.OSHUT)) {
                            // If somebody else switched to CLOSED while we were oshutting,
                            // then we do the close for them
                            if (_state.get() == State.CLOSED)
                                doOnClose(null);
                            else
                                throw new IllegalStateException();
                        }
                    }
                    return;
                case ISHUTTING:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    // The thread doing the ISHUT will close
                    return;
                case ISHUT:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    // Already ISHUT so we close
                    doOnClose(null);
                    return;
                // Somebody else oshutting
                case OSHUTTING:
                case // Already oshut
                OSHUT:
                    return;
                case // already closed
                CLOSED:
                    return;
            }
        }
    }

    @Override
    public final void close() {
        if (LOG.isDebugEnabled())
            LOG.debug("close {}", this);
        close(null);
    }

    protected final void close(Throwable failure) {
        if (LOG.isDebugEnabled())
            LOG.debug("close({}) {}", failure, this);
        while (true) {
            State s = _state.get();
            switch(s) {
                case OPEN:
                // Already ishut
                case ISHUT:
                case // Already oshut
                OSHUT:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    doOnClose(failure);
                    return;
                // Somebody else ishutting
                case ISHUTTING:
                case // Somebody else oshutting
                OSHUTTING:
                    if (!_state.compareAndSet(s, State.CLOSED))
                        continue;
                    // The thread doing the IO SHUT will call doOnClose
                    return;
                case // already closed
                CLOSED:
                    return;
            }
        }
    }

    protected void doShutdownInput() {
    }

    protected void doShutdownOutput() {
    }

    private void doOnClose(Throwable failure) {
        try {
            doClose();
        } finally {
            if (failure == null)
                onClose();
            else
                onClose(failure);
        }
    }

    protected void doClose() {
    }

    protected void onClose(Throwable failure) {
        super.onClose();
        _writeFlusher.onFail(failure);
        _fillInterest.onFail(failure);
    }

    @Override
    public boolean isOutputShutdown() {
        switch(_state.get()) {
            case CLOSED:
            case OSHUT:
            case OSHUTTING:
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean isInputShutdown() {
        switch(_state.get()) {
            case CLOSED:
            case ISHUT:
            case ISHUTTING:
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean isOpen() {
        switch(_state.get()) {
            case CLOSED:
                return false;
            default:
                return true;
        }
    }

    public void checkFlush() throws IOException {
        State s = _state.get();
        switch(s) {
            case OSHUT:
            case OSHUTTING:
            case CLOSED:
                throw new IOException(s.toString());
            default:
                break;
        }
    }

    public void checkFill() throws IOException {
        State s = _state.get();
        switch(s) {
            case ISHUT:
            case ISHUTTING:
            case CLOSED:
                throw new IOException(s.toString());
            default:
                break;
        }
    }

    @Override
    public long getCreatedTimeStamp() {
        return _created;
    }

    @Override
    public Connection getConnection() {
        return _connection;
    }

    @Override
    public void setConnection(Connection connection) {
        _connection = connection;
    }

    @Override
    public boolean isOptimizedForDirectBuffers() {
        return false;
    }

    protected void reset() {
        _state.set(State.OPEN);
        _writeFlusher.onClose();
        _fillInterest.onClose();
    }

    @Override
    public void onOpen() {
        if (LOG.isDebugEnabled())
            LOG.debug("onOpen {}", this);
        if (_state.get() != State.OPEN)
            throw new IllegalStateException();
    }

    @Override
    public void onClose() {
        super.onClose();
        _writeFlusher.onClose();
        _fillInterest.onClose();
    }

    @Override
    public void fillInterested(Callback callback) {
        notIdle();
        _fillInterest.register(callback);
    }

    @Override
    public boolean tryFillInterested(Callback callback) {
        notIdle();
        return _fillInterest.tryRegister(callback);
    }

    @Override
    public boolean isFillInterested() {
        return _fillInterest.isInterested();
    }

    @Override
    public void write(Callback callback, ByteBuffer... buffers) throws IllegalStateException {
        _writeFlusher.write(callback, buffers);
    }

    protected abstract void onIncompleteFlush();

    protected abstract void needsFillInterest() throws IOException;

    public FillInterest getFillInterest() {
        return _fillInterest;
    }

    public WriteFlusher getWriteFlusher() {
        return _writeFlusher;
    }

    @Override
    protected void onIdleExpired(TimeoutException timeout) {
        Connection connection = _connection;
        if (connection != null && !connection.onIdleExpired())
            return;
        boolean outputShutdown = isOutputShutdown();
        boolean inputShutdown = isInputShutdown();
        boolean fillFailed = _fillInterest.onFail(timeout);
        boolean writeFailed = _writeFlusher.onFail(timeout);
        // If the endpoint is half closed and there was no fill/write handling, then close here.
        // This handles the situation where the connection has completed its close handling
        // and the endpoint is half closed, but the other party does not complete the close.
        // This perhaps should not check for half closed, however the servlet spec case allows
        // for a dispatched servlet or suspended request to extend beyond the connections idle
        // time.  So if this test would always close an idle endpoint that is not handled, then
        // we would need a mode to ignore timeouts for some HTTP states
        if (isOpen() && (outputShutdown || inputShutdown) && !(fillFailed || writeFailed))
            close();
        else
            LOG.debug("Ignored idle endpoint {}", this);
    }

    @Override
    public void upgrade(Connection newConnection) {
        Connection oldConnection = getConnection();
        if (LOG.isDebugEnabled())
            LOG.debug("{} upgrading from {} to {}", this, oldConnection, newConnection);
        ByteBuffer buffer = (oldConnection instanceof Connection.UpgradeFrom) ? ((Connection.UpgradeFrom) oldConnection).onUpgradeFrom() : null;
        oldConnection.onClose();
        oldConnection.getEndPoint().setConnection(newConnection);
        if (BufferUtil.hasContent(buffer)) {
            if (newConnection instanceof Connection.UpgradeTo)
                ((Connection.UpgradeTo) newConnection).onUpgradeTo(buffer);
            else
                throw new IllegalStateException("Cannot upgrade: " + newConnection + " does not implement " + Connection.UpgradeTo.class.getName());
        }
        newConnection.onOpen();
    }

    @Override
    public String toString() {
        return String.format("%s->%s", toEndPointString(), toConnectionString());
    }

    public String toEndPointString() {
        Class c = getClass();
        String name = c.getSimpleName();
        while (name.length() == 0 && c.getSuperclass() != null) {
            c = c.getSuperclass();
            name = c.getSimpleName();
        }
        return String.format("%s@%h{l=%s,r=%s,%s,fill=%s,flush=%s,to=%d/%d}", name, this, getLocalAddress(), getRemoteAddress(), _state.get(), _fillInterest.toStateString(), _writeFlusher.toStateString(), getIdleFor(), getIdleTimeout());
    }

    public String toConnectionString() {
        Connection connection = getConnection();
        if (// can happen during upgrade
        connection == null)
            return "";
        if (connection instanceof AbstractConnection)
            return ((AbstractConnection) connection).toConnectionString();
        return String.format("%s@%x", connection.getClass().getSimpleName(), connection.hashCode());
    }

    private enum State {

        OPEN,
        ISHUTTING,
        ISHUT,
        OSHUTTING,
        OSHUT,
        CLOSED
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy