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

com.mongodb.reactivestreams.client.internal.GridFSUploadPublisherImpl Maven / Gradle / Ivy

/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * 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 com.mongodb.reactivestreams.client.internal;

import com.mongodb.Block;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.async.client.gridfs.AsyncGridFSUploadStream;
import com.mongodb.reactivestreams.client.gridfs.GridFSUploadPublisher;
import org.bson.BsonValue;
import org.bson.types.ObjectId;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.nio.ByteBuffer;


public class GridFSUploadPublisherImpl implements GridFSUploadPublisher {
    private final AsyncGridFSUploadStream gridFSUploadStream;
    private final Publisher source;

    GridFSUploadPublisherImpl(final AsyncGridFSUploadStream gridFSUploadStream, final Publisher source) {
        this.gridFSUploadStream = gridFSUploadStream;
        this.source = source;
    }

    @Override
    public ObjectId getObjectId() {
        return gridFSUploadStream.getObjectId();
    }

    @Override
    public BsonValue getId() {
        return gridFSUploadStream.getId();
    }

    @Override
    public void subscribe(final Subscriber s) {
        s.onSubscribe(new GridFSUploadSubscription(s));
    }

    class GridFSUploadSubscription implements Subscription {
        private final Subscriber outerSubscriber;

        /* protected by `this` */
        private boolean hasCompleted;
        private boolean unsubscribed;
        private Action currentAction = Action.WAITING;
        private Subscription sourceSubscription;
        /* protected by `this` */

        GridFSUploadSubscription(final Subscriber outerSubscriber) {
            this.outerSubscriber = outerSubscriber;
        }

        private final Subscriber sourceSubscriber = new Subscriber() {
            @Override
            public void onSubscribe(final Subscription s) {
                synchronized (GridFSUploadSubscription.this) {
                    sourceSubscription = s;
                    currentAction = Action.WAITING;
                }
                tryProcess();
            }

            @Override
            public void onNext(final ByteBuffer byteBuffer) {
                synchronized (GridFSUploadSubscription.this) {
                    currentAction = Action.IN_PROGRESS;
                }
                Publishers.publish((Block>) callback -> gridFSUploadStream.write(byteBuffer, callback))
                        .subscribe(new GridFSUploadStreamSubscriber(byteBuffer));
            }

            @Override
            public void onError(final Throwable t) {
                synchronized (GridFSUploadSubscription.this) {
                    currentAction = Action.FINISHED;
                }
                outerSubscriber.onError(t);
            }

            @Override
            public void onComplete() {
                synchronized (GridFSUploadSubscription.this) {
                    hasCompleted = true;
                    if (currentAction == Action.REQUESTING_MORE) {
                        currentAction = Action.COMPLETE;
                        tryProcess();
                    }
                }
            }


            class GridFSUploadStreamSubscriber implements Subscriber {
                private final ByteBuffer byteBuffer;

                GridFSUploadStreamSubscriber(final ByteBuffer byteBuffer) {
                    this.byteBuffer = byteBuffer;
                }

                @Override
                public void onSubscribe(final Subscription s) {
                    s.request(1);
                }

                @Override
                public void onNext(final Integer integer) {
                }

                @Override
                public void onError(final Throwable t) {
                    terminate();
                    outerSubscriber.onError(t);
                }

                @Override
                public void onComplete() {
                    if (byteBuffer.remaining() > 0) {
                        sourceSubscriber.onNext(byteBuffer);
                    } else {
                        synchronized (GridFSUploadSubscription.this) {
                            if (hasCompleted) {
                                currentAction = Action.COMPLETE;
                            }
                            if (unsubscribed) {
                                currentAction = Action.TERMINATE;
                            }
                            if (currentAction != Action.COMPLETE && currentAction != Action.TERMINATE && currentAction != Action.FINISHED) {
                                currentAction = Action.WAITING;
                            }
                        }
                        tryProcess();
                    }
                }
            }
        };

        @Override
        public void request(final long n) {
            boolean isUnsubscribed;
            synchronized (this) {
                isUnsubscribed = unsubscribed;
                if (!isUnsubscribed && n < 1) {
                    currentAction = Action.FINISHED;
                }
            }
            if (!isUnsubscribed && n < 1) {
                outerSubscriber.onError(new IllegalArgumentException("3.9 While the Subscription is not cancelled, "
                        + "Subscription.request(long n) MUST throw a java.lang.IllegalArgumentException if the "
                        + "argument is <= 0."));
                return;
            }
            tryProcess();
        }

        @Override
        public void cancel() {
            synchronized (this) {
                unsubscribed = true;
            }
            terminate();
        }

        private void tryProcess() {
            NextStep nextStep;
            synchronized (this) {
                switch (currentAction) {
                    case WAITING:
                        if (sourceSubscription == null) {
                            nextStep = NextStep.SUBSCRIBE;
                        } else {
                            nextStep = NextStep.REQUEST_MORE;
                        }
                        currentAction = Action.REQUESTING_MORE;
                        break;
                    case COMPLETE:
                        nextStep = NextStep.COMPLETE;
                        currentAction = Action.FINISHED;
                        break;
                    case TERMINATE:
                        nextStep = NextStep.TERMINATE;
                        currentAction = Action.FINISHED;
                        break;
                    case REQUESTING_MORE:
                    case IN_PROGRESS:
                    case FINISHED:
                    default:
                        nextStep = NextStep.DO_NOTHING;
                        break;
                }
            }

            switch (nextStep) {
                case SUBSCRIBE:
                    source.subscribe(sourceSubscriber);
                    break;
                case REQUEST_MORE:
                    synchronized (this) {
                        sourceSubscription.request(1);
                    }
                    break;
                case COMPLETE:
                    Publishers.publish(gridFSUploadStream::close).subscribe(new Subscriber() {
                        @Override
                        public void onSubscribe(final Subscription s) {
                            s.request(1);
                        }

                        @Override
                        public void onNext(final Void success) {
                            outerSubscriber.onNext(success);
                        }

                        @Override
                        public void onError(final Throwable t) {
                            outerSubscriber.onError(t);
                        }

                        @Override
                        public void onComplete() {
                            outerSubscriber.onComplete();
                        }
                    });
                    break;
                case TERMINATE:
                    Publishers.publish(gridFSUploadStream::abort).subscribe(new Subscriber() {
                        @Override
                        public void onSubscribe(final Subscription s) {
                            s.request(1);
                        }

                        @Override
                        public void onNext(final Void success) {
                        }

                        @Override
                        public void onError(final Throwable t) {
                        }

                        @Override
                        public void onComplete() {
                        }
                    });
                    break;
                case DO_NOTHING:
                default:
                    break;
            }
        }

        private void terminate() {
            synchronized (this) {
                currentAction = Action.TERMINATE;
            }
            tryProcess();
        }
    }

    GridFSUploadPublisher withObjectId() {
        final GridFSUploadPublisherImpl wrapped = this;
        return new GridFSUploadPublisher() {

            @Override
            public ObjectId getObjectId() {
                return wrapped.getObjectId();
            }

            @Override
            public BsonValue getId() {
                return wrapped.getId();
            }

            @Override
            public void subscribe(final Subscriber objectIdSub) {
                wrapped.subscribe(new Subscriber() {
                    @Override
                    public void onSubscribe(final Subscription s) {
                        objectIdSub.onSubscribe(s);
                    }

                    @Override
                    public void onNext(final Void success) {
                    }

                    @Override
                    public void onError(final Throwable t) {
                        objectIdSub.onError(t);
                    }

                    @Override
                    public void onComplete() {
                        objectIdSub.onNext(getObjectId());
                        objectIdSub.onComplete();
                    }
                });
            }
        };
    }

    enum Action {
        WAITING,
        REQUESTING_MORE,
        IN_PROGRESS,
        TERMINATE,
        COMPLETE,
        FINISHED
    }

    enum NextStep {
        SUBSCRIBE,
        REQUEST_MORE,
        COMPLETE,
        TERMINATE,
        DO_NOTHING
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy