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

org.eclipse.jetty.io.AbstractConnection 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.util.EventListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeoutException;

import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.Invocable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

A convenience base implementation of {@link Connection}.

*

This class uses the capabilities of the {@link EndPoint} API to provide a * more traditional style of async reading. A call to {@link #fillInterested()} * will schedule a callback to {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)} * as appropriate.

*/ public abstract class AbstractConnection implements Connection, Invocable { private static final Logger LOG = LoggerFactory.getLogger(AbstractConnection.class); private final List _listeners = new CopyOnWriteArrayList<>(); private final long _created = System.currentTimeMillis(); private final EndPoint _endPoint; private final Executor _executor; private final Callback _readCallback; private int _inputBufferSize = 2048; protected AbstractConnection(EndPoint endPoint, Executor executor) { if (executor == null) throw new IllegalArgumentException("Executor must not be null!"); _endPoint = endPoint; _executor = executor; _readCallback = new ReadCallback(); } @Deprecated @Override public InvocationType getInvocationType() { // TODO consider removing the #fillInterested method from the connection and only use #fillInterestedCallback // so a connection need not be Invocable return Invocable.super.getInvocationType(); } @Override public void addEventListener(EventListener listener) { if (listener instanceof Listener) _listeners.add((Listener)listener); } @Override public void removeEventListener(EventListener eventListener) { if (eventListener instanceof Listener listener) _listeners.remove(listener); } public int getInputBufferSize() { return _inputBufferSize; } public void setInputBufferSize(int inputBufferSize) { _inputBufferSize = inputBufferSize; } protected Executor getExecutor() { return _executor; } protected void failedCallback(final Callback callback, final Throwable x) { Runnable failCallback = () -> { try { callback.failed(x); } catch (Exception e) { LOG.warn("Failed callback", x); } }; switch (Invocable.getInvocationType(callback)) { case BLOCKING: try { getExecutor().execute(failCallback); } catch (RejectedExecutionException e) { LOG.debug("Rejected", e); callback.failed(x); } break; case NON_BLOCKING: failCallback.run(); break; case EITHER: Invocable.invokeNonBlocking(failCallback); break; default: throw new IllegalStateException(); } } /** *

Utility method to be called to register read interest.

*

After a call to this method, {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)} * will be called back as appropriate.

* * @see #onFillable() */ public void fillInterested() { if (LOG.isDebugEnabled()) LOG.debug("fillInterested {}", this); getEndPoint().fillInterested(_readCallback); } public void tryFillInterested(Callback callback) { getEndPoint().tryFillInterested(callback); } public boolean isFillInterested() { return getEndPoint().isFillInterested(); } /** *

Callback method invoked when the endpoint is ready to be read.

* * @see #fillInterested() */ public abstract void onFillable(); /** *

Callback method invoked when the endpoint failed to be ready to be read.

* * @param cause the exception that caused the failure */ protected void onFillInterestedFailed(Throwable cause) { if (LOG.isDebugEnabled()) LOG.debug("{} onFillInterestedFailed {}", this, cause); if (_endPoint.isOpen()) { boolean close = true; if (cause instanceof TimeoutException timeout) close = onReadTimeout(timeout); if (close) { if (_endPoint.isOutputShutdown()) _endPoint.close(); else { _endPoint.shutdownOutput(); fillInterested(); } } } } /** *

Callback method invoked when the endpoint failed to be ready to be read after a timeout

* * @param timeout the cause of the read timeout * @return true to signal that the endpoint must be closed, false to keep the endpoint open */ protected boolean onReadTimeout(TimeoutException timeout) { return true; } @Override public void onOpen() { if (LOG.isDebugEnabled()) LOG.debug("onOpen {}", this); for (Listener listener : _listeners) { onOpened(listener); } } private void onOpened(Listener listener) { try { listener.onOpened(this); } catch (Throwable x) { LOG.info("Failure while notifying listener {}", listener, x); } } @Override public void onClose(Throwable cause) { if (LOG.isDebugEnabled()) { if (cause == null) LOG.debug("onClose {}", this); else LOG.debug("onClose {}", this, cause); } for (Listener listener : _listeners) { onClosed(listener); } } private void onClosed(Listener listener) { try { listener.onClosed(this); } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.info("Failure while notifying listener {}", listener, x); else LOG.info("Failure while notifying listener {} {}", listener, x.toString()); } } @Override public EndPoint getEndPoint() { return _endPoint; } @Override public void close() { getEndPoint().close(); } @Override public boolean onIdleExpired(TimeoutException timeoutException) { return true; } @Override public long getMessagesIn() { return -1; } @Override public long getMessagesOut() { return -1; } @Override public long getBytesIn() { return -1; } @Override public long getBytesOut() { return -1; } @Override public long getCreatedTimeStamp() { return _created; } @Override public final String toString() { return String.format("%s@%x::%s", getClass().getSimpleName(), hashCode(), getEndPoint()); } public String toConnectionString() { return String.format("%s@%x", getClass().getSimpleName(), hashCode()); } private class ReadCallback implements Callback, Invocable { @Override public void succeeded() { onFillable(); } @Override public void failed(Throwable x) { onFillInterestedFailed(x); } @Override public String toString() { return String.format("%s@%x{%s}", getClass().getSimpleName(), hashCode(), AbstractConnection.this); } @Override public InvocationType getInvocationType() { return AbstractConnection.this.getInvocationType(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy