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

org.eclipse.jetty.client.util.AbstractRequestContent Maven / Gradle / Ivy

//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.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.client.util;

import java.nio.ByteBuffer;

import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

Partial implementation of {@link Request.Content}.

*/ public abstract class AbstractRequestContent implements Request.Content { private static final Logger LOG = LoggerFactory.getLogger(AbstractRequestContent.class); private final AutoLock lock = new AutoLock(); private final String contentType; protected AbstractRequestContent(String contentType) { this.contentType = contentType; } @Override public String getContentType() { return contentType; } @Override public Subscription subscribe(Consumer consumer, boolean emitInitialContent) { Subscription subscription = newSubscription(consumer, emitInitialContent); if (LOG.isDebugEnabled()) LOG.debug("Content subscription for {}: {}", subscription, consumer); return subscription; } protected abstract Subscription newSubscription(Consumer consumer, boolean emitInitialContent); /** *

Partial implementation of {@code Subscription}.

*

Implements the algorithm described in {@link Request.Content}.

*/ public abstract class AbstractSubscription implements Subscription { private final Consumer consumer; private final boolean emitInitialContent; private Throwable failure; private int demand; // Whether content production was stalled because there was no demand. private boolean stalled; // Whether the first content has been produced. private boolean committed; public AbstractSubscription(Consumer consumer, boolean emitInitialContent) { this.consumer = consumer; this.emitInitialContent = emitInitialContent; this.stalled = true; } @Override public void demand() { boolean produce; try (AutoLock ignored = lock.lock()) { ++demand; produce = stalled; if (stalled) stalled = false; } if (LOG.isDebugEnabled()) LOG.debug("Content demand, producing {} for {}", produce, this); if (produce) produce(); } private void produce() { while (true) { Throwable failure; boolean committed; try (AutoLock ignored = lock.lock()) { failure = this.failure; committed = this.committed; } if (failure != null) { notifyFailure(failure); return; } if (committed || emitInitialContent) { try { if (!produceContent(this::processContent)) return; } catch (Throwable x) { // Fail and loop around to notify the failure. fail(x); } } else { if (!processContent(BufferUtil.EMPTY_BUFFER, false, Callback.NOOP)) return; } } } /** *

Subclasses implement this method to produce content, * without worrying about demand or exception handling.

*

Typical implementation (pseudo code):

*
         * protected boolean produceContent(Producer producer) throws Exception
         * {
         *     // Step 1: try to produce content, exceptions may be thrown during production
         *     //  (for example, producing content reading from an InputStream may throw).
         *
         *     // Step 2A: content could be produced.
         *     ByteBuffer buffer = ...;
         *     boolean last = ...;
         *     Callback callback = ...;
         *     return producer.produce(buffer, last, callback);
         *
         *     // Step 2B: content could not be produced.
         *     //  (for example it is not available yet)
         *     return false;
         * }
         * 
* * @param producer the producer to notify when content can be produced * @return whether content production should continue * @throws Exception when content production fails */ protected abstract boolean produceContent(Producer producer) throws Exception; @Override public void fail(Throwable failure) { try (AutoLock ignored = lock.lock()) { if (this.failure == null) this.failure = failure; } } private boolean processContent(ByteBuffer content, boolean last, Callback callback) { try (AutoLock ignored = lock.lock()) { committed = true; --demand; } if (content != null) notifyContent(content, last, callback); else callback.succeeded(); boolean noDemand; try (AutoLock ignored = lock.lock()) { noDemand = demand == 0; if (noDemand) stalled = true; } if (noDemand) { if (LOG.isDebugEnabled()) LOG.debug("No demand, processing stalled for {}", this); return false; } return true; } protected void notifyContent(ByteBuffer buffer, boolean last, Callback callback) { try { if (LOG.isDebugEnabled()) LOG.debug("Notifying content last={} {} for {}", last, BufferUtil.toDetailString(buffer), this); consumer.onContent(buffer, last, callback); } catch (Throwable x) { callback.failed(x); fail(x); } } private void notifyFailure(Throwable failure) { try { if (LOG.isDebugEnabled()) LOG.debug("Notifying failure for {}", this, failure); consumer.onFailure(failure); } catch (Exception x) { LOG.trace("Failure while notifying content failure {}", failure, x); } } @Override public String toString() { int demand; boolean stalled; boolean committed; try (AutoLock ignored = lock.lock()) { demand = this.demand; stalled = this.stalled; committed = this.committed; } return String.format("%s.%s@%x[demand=%d,stalled=%b,committed=%b,emitInitial=%b]", getClass().getEnclosingClass().getSimpleName(), getClass().getSimpleName(), hashCode(), demand, stalled, committed, emitInitialContent); } } public interface Producer { boolean produce(ByteBuffer content, boolean lastContent, Callback callback); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy