org.springframework.reactive.web.servlet.RequestBodyPublisher.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.reactive.web.servlet.RequestBodyPublisher.java

Source

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.reactive.web.servlet;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import org.springframework.reactive.util.DemandCounter;

/**
 * @author Arjen Poutsma
 */
public class RequestBodyPublisher implements ReadListener, Publisher<byte[]> {

    private final Charset UTF_8 = Charset.forName("UTF-8");

    private static final Log logger = LogFactory.getLog(RequestBodyPublisher.class);

    private final AsyncContextSynchronizer synchronizer;

    private final byte[] buffer;

    private final DemandCounter demand = new DemandCounter();

    private Subscriber<? super byte[]> subscriber;

    public RequestBodyPublisher(AsyncContextSynchronizer synchronizer, int bufferSize) {
        this.synchronizer = synchronizer;
        this.buffer = new byte[bufferSize];
    }

    @Override
    public void subscribe(Subscriber<? super byte[]> s) {
        this.subscriber = s;

        this.subscriber.onSubscribe(new RequestBodySubscription());
    }

    @Override
    public void onDataAvailable() throws IOException {
        ServletInputStream input = this.synchronizer.getInputStream();

        while (true) {
            logger.debug("Demand: " + this.demand);

            if (!demand.hasDemand()) {
                break;
            }

            boolean ready = input.isReady();
            logger.debug("Input " + ready + "/" + input.isFinished());

            if (!ready) {
                break;
            }

            int read = input.read(buffer);
            logger.debug("Input read:" + read);

            if (read == -1) {
                break;
            } else if (read > 0) {
                this.demand.decrement();
                byte[] copy = Arrays.copyOf(this.buffer, read);

                //            logger.debug("Next: " + new String(copy, UTF_8));

                this.subscriber.onNext(copy);

            }
        }
    }

    @Override
    public void onAllDataRead() throws IOException {
        logger.debug("All data read");
        this.synchronizer.readComplete();
        this.subscriber.onComplete();
    }

    @Override
    public void onError(Throwable t) {
        logger.error("RequestBodyPublisher Error", t);
        this.subscriber.onError(t);
    }

    private class RequestBodySubscription implements Subscription {

        @Override
        public void request(long n) {
            logger.debug("Updating demand " + demand + " by " + n);

            boolean stalled = !demand.hasDemand();

            demand.increase(n);

            if (stalled) {
                try {
                    onDataAvailable();
                } catch (IOException ex) {
                    onError(ex);
                }
            }
        }

        @Override
        public void cancel() {
            synchronizer.readComplete();
            demand.reset();
        }
    }
}