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

com.couchbase.client.core.util.CompositeStateful Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright (c) 2019 Couchbase, 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.couchbase.client.core.util;

import com.couchbase.client.core.endpoint.KeyValueEndpoint;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * Represents a stateful component of one or more individual stateful elements.
 */
public class CompositeStateful implements Stateful {

  private final OUT initialState;
  private final Map states;
  private final SingleStateful inner;
  private final Map subscriptions;
  private final BiConsumer beforeTransitionCallback;
  private final Function, OUT> transformer;

  private CompositeStateful(final OUT initialState, final Function, OUT> transformer,
                            final BiConsumer beforeTransitionCallback) {
    this.inner = SingleStateful.fromInitial(initialState);
    this.initialState = initialState;
    this.transformer = transformer;
    this.subscriptions = new ConcurrentHashMap<>();
    this.states = new ConcurrentHashMap<>();
    this.beforeTransitionCallback = beforeTransitionCallback;
  }

  /**
   * Creates a new transformer with an initial state and the transform function that should be applied.
   *
   * @param initialState the initial state.
   * @param transformer the custom transformer for the states.
   * @return a created stateful composite.
   */
  public static  CompositeStateful create(final OUT initialState,
                                                                  final Function, OUT> transformer,
                                                                  final BiConsumer beforeTransitionCallback) {
    return new CompositeStateful<>(initialState, transformer, beforeTransitionCallback);
  }

  /**
   * Creates a new transformer with an initial state and the transform function that should be applied.
   *
   * @param initialState the initial state.
   * @param transformer the custom transformer for the states.
   * @return a created stateful composite.
   */
  public static  CompositeStateful create(final OUT initialState,
                                                                  final Function, OUT> transformer) {
    return create(initialState, transformer, (oldState, newState) -> {});
  }

  /**
   * Registers a stateful element with the composite.
   *
   * @param identifier the unique identifier to use.
   * @param upstream the upstream flux with the state stream.
   */
  public synchronized void register(final T identifier, final Stateful upstream) {
    states.put(identifier, upstream.state());
    transition(transformer.apply(states.values()));

    Disposable subscription = upstream.states().subscribe(
      s -> {
        states.put(identifier, s);
        transition(transformer.apply(states.values()));
      },
      e -> deregister(identifier),
      () -> deregister(identifier)
    );

    subscriptions.put(identifier, subscription);
  }

  /**
   * Deregisters a stateful element from the composite.
   *
   * 

Note that it might be possible that the passed in identifier is already deregistered (for example if * the upstream flux already completed or failed). In this case, this is essentially a "noop" since the target state, * the identifier not being part of the stateful component, is already reached.

* * @param identifier the unique identifier to use. */ public synchronized void deregister(final T identifier) { Disposable subscription = subscriptions.remove(identifier); if (subscription != null && !subscription.isDisposed()) { subscription.dispose(); states.remove(identifier); transition(transformer.apply(states.values())); } if (subscriptions.isEmpty()) { transition(initialState); } } private void transition(final OUT newState) { if (!inner.state().equals(newState)) { beforeTransitionCallback.accept(inner.state(), newState); inner.transition(newState); } } /** * Closes the composite permanently and deregisters all elements. */ public synchronized void close() { Set identifiers = new HashSet<>(subscriptions.keySet()); for (T identifier : identifiers) { deregister(identifier); } inner.close(); } @Override public OUT state() { return inner.state(); } @Override public Flux states() { return inner.states(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy