/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *        https://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.
 */

/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *        https://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 reactor.core.publisher;

import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;

/**
 * Utilities around manipulating stack traces and displaying assembly traces.
 *
 * @author Simon Baslé
 * @author Sergei Egorov
 */
final class Traces {

	/**
	 * If set to true, the creation of FluxOnAssembly will capture the raw stacktrace
	 * instead of the sanitized version.
	 */
	static final boolean full = Boolean.parseBoolean(System.getProperty(
			"reactor.trace.assembly.fullstacktrace",
			"false"));

	/**
	 * Transform the current stack trace into a {@link String} representation,
	 * each element being prepended with a tabulation and appended with a
	 * newline.
	 */
	static Supplier<Supplier<String>> callSiteSupplierFactory;

	static {
		String[] strategyClasses = {
				Traces.class.getName() + "$StackWalkerCallSiteSupplierFactory",
				Traces.class.getName() + "$SharedSecretsCallSiteSupplierFactory",
				Traces.class.getName() + "$ExceptionCallSiteSupplierFactory",
		};
		callSiteSupplierFactory = Stream
				.of(strategyClasses)
				.flatMap(className -> {
					try {
						Class<?> clazz = Class.forName(className);
						@SuppressWarnings("unchecked")
						Supplier<Supplier<String>> function = (Supplier) clazz.getDeclaredConstructor()
						                                                      .newInstance();
						return Stream.of(function);
					}
					catch (Throwable e) {
						return Stream.empty();
					}
				})
				.findFirst()
				.orElseThrow(() -> new IllegalStateException("Valid strategy not found"));
	}

	/**
	 * Utility class for the call-site extracting on Java 9+.
	 *
	 */
	@SuppressWarnings("unused")
	static final class StackWalkerCallSiteSupplierFactory implements Supplier<Supplier<String>> {

		static {
			// Trigger eager StackWalker class loading.
			StackWalker.getInstance();
		}

		/**
		 * Transform the current stack trace into a {@link String} representation,
		 * each element being prepended with a tabulation and appended with a
		 * newline.
		 *
		 * @return the string version of the stacktrace.
		 */
		@Override
		public Supplier<String> get() {
			StackWalker.StackFrame[] stack = StackWalker.getInstance().walk(s -> {
				StackWalker.StackFrame[] result = new StackWalker.StackFrame[10];
				Iterator<StackWalker.StackFrame> iterator = s.iterator();
				iterator.next(); // .get

				int i = 0;
				while (iterator.hasNext()) {
					StackWalker.StackFrame frame = iterator.next();

					if (i >= result.length) {
						return new StackWalker.StackFrame[0];
					}

					result[i++] = frame;

					if (isUserCode(frame.getClassName())) {
						break;
					}
				}
				StackWalker.StackFrame[] copy = new StackWalker.StackFrame[i];
				System.arraycopy(result, 0, copy, 0, i);
				return copy;
			});

			if (stack.length == 0) {
				return () -> "";
			}

			if (stack.length == 1) {
				return () -> "\t" + stack[0].toString() + "\n";
			}

			return () -> {
				StringBuilder sb = new StringBuilder();

				for (int j = stack.length - 2; j > 0; j--) {
					StackWalker.StackFrame previous = stack[j];

					if (!full) {
						if (previous.isNativeMethod()) {
							continue;
						}

						String previousRow = previous.getClassName() + "." + previous.getMethodName();
						if (shouldSanitize(previousRow)) {
							continue;
						}
					}
					sb.append("\t")
					  .append(previous.toString())
					  .append("\n");
					break;
				}

				sb.append("\t")
				  .append(stack[stack.length - 1].toString())
				  .append("\n");

				return sb.toString();
			};
		}
	}

	@SuppressWarnings("unused")
	static class SharedSecretsCallSiteSupplierFactory implements Supplier<Supplier<String>> {

		@Override
		public Supplier<String> get() {
			return new TracingException();
		}

		static class TracingException extends Throwable implements Supplier<String> {

			static final JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();

			@Override
			public String get() {
				int stackTraceDepth = javaLangAccess.getStackTraceDepth(this);

				StackTraceElement previousElement = null;
				// Skip get()
				for (int i = 2; i < stackTraceDepth; i++) {
					StackTraceElement e = javaLangAccess.getStackTraceElement(this, i);

					String className = e.getClassName();
					if (isUserCode(className)) {
						StringBuilder sb = new StringBuilder();

						if (previousElement != null) {
							sb.append("\t").append(previousElement.toString()).append("\n");
						}
						sb.append("\t").append(e.toString()).append("\n");
						return sb.toString();
					}
					else {
						if (!full && e.getLineNumber() <= 1) {
							continue;
						}

						String classAndMethod = className + "." + e.getMethodName();
						if (!full && shouldSanitize(classAndMethod)) {
							continue;
						}
						previousElement = e;
					}
				}

				return "";
			}
		}
	}

	@SuppressWarnings("unused")
	static class ExceptionCallSiteSupplierFactory implements Supplier<Supplier<String>> {

		@Override
		public Supplier<String> get() {
			return new TracingException();
		}

		static class TracingException extends Throwable implements Supplier<String> {

			@Override
			public String get() {
				StackTraceElement previousElement = null;
				StackTraceElement[] stackTrace = getStackTrace();
				// Skip get()
				for (int i = 2; i < stackTrace.length; i++) {
					StackTraceElement e = stackTrace[i];

					String className = e.getClassName();
					if (isUserCode(className)) {
						StringBuilder sb = new StringBuilder();

						if (previousElement != null) {
							sb.append("\t").append(previousElement.toString()).append("\n");
						}
						sb.append("\t").append(e.toString()).append("\n");
						return sb.toString();
					}
					else {
						if (!full && e.getLineNumber() <= 1) {
							continue;
						}

						String classAndMethod = className + "." + e.getMethodName();
						if (!full && shouldSanitize(classAndMethod)) {
							continue;
						}
						previousElement = e;
					}
				}

				return "";
			}
		}
	}

	/**
	 * Return true for strings (usually from a stack trace element) that should be
	 * sanitized out by {@link Traces#callSiteSupplierFactory}.
	 *
	 * @param stackTraceRow the row to check
	 * @return true if it should be sanitized out, false if it should be kept
	 */
	static boolean shouldSanitize(String stackTraceRow) {
		return stackTraceRow.startsWith("java.util.function")
				|| stackTraceRow.startsWith("reactor.core.publisher.Mono.onAssembly")
				|| stackTraceRow.equals("reactor.core.publisher.Mono.onAssembly")
				|| stackTraceRow.equals("reactor.core.publisher.Flux.onAssembly")
				|| stackTraceRow.equals("reactor.core.publisher.ParallelFlux.onAssembly")
				|| stackTraceRow.startsWith("reactor.core.publisher.SignalLogger")
				|| stackTraceRow.startsWith("reactor.core.publisher.FluxOnAssembly")
				|| stackTraceRow.startsWith("reactor.core.publisher.MonoOnAssembly.")
				|| stackTraceRow.startsWith("reactor.core.publisher.MonoCallableOnAssembly.")
				|| stackTraceRow.startsWith("reactor.core.publisher.FluxCallableOnAssembly.")
				|| stackTraceRow.startsWith("reactor.core.publisher.Hooks")
				|| stackTraceRow.startsWith("sun.reflect")
				|| stackTraceRow.startsWith("java.util.concurrent.ThreadPoolExecutor")
				|| stackTraceRow.startsWith("java.lang.reflect");
	}

	/**
	 * Extract operator information out of an assembly stack trace in {@link String} form
	 * (see {@link Traces#callSiteSupplierFactory}).
	 *

	 * Most operators will result in a line of the form {@code "Flux.map ⇢ user.code.Class.method(Class.java:123)"},
	 * that is:
	 *

	 *
The top of the stack is inspected for Reactor API references, and the deepest
	 *     one is kept, since multiple API references generally denote an alias operator.
	 *     (eg. {@code "Flux.map"})

	 *
The next stacktrace element is considered user code and is appended to the
	 *     result with a {@code ⇢} separator. (eg. {@code " ⇢ user.code.Class.method(Class.java:123)"})

	 *
If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.

	 *
If the sanitized stack is empty, returns {@code "[no operator assembly information]"}

	 *

	 *
	 *
	 * @param source the sanitized assembly stacktrace in String format.
	 * @return a {@link String} representing operator and operator assembly site extracted
	 * from the assembly stack trace.
	 */
	static String extractOperatorAssemblyInformation(String source) {
		return extractOperatorAssemblyInformation(source, false);
	}

	static boolean isUserCode(String line) {
		return !line.startsWith("reactor.core.publisher") || line.contains("Test");
	}

	/**
	 * Extract operator information out of an assembly stack trace in {@link String} form
	 * (see {@link Traces#callSiteSupplierFactory}) which potentially
	 * has a header line that one can skip by setting {@code skipFirst} to {@code true}.
	 *

	 * Most operators will result in a line of the form {@code "Flux.map ⇢ user.code.Class.method(Class.java:123)"},
	 * that is:
	 *

	 *
The top of the stack is inspected for Reactor API references, and the deepest
	 *     one is kept, since multiple API references generally denote an alias operator.
	 *     (eg. {@code "Flux.map"})

	 *
The next stacktrace element is considered user code and is appended to the
	 *     result with a {@code ⇢} separator. (eg. {@code " ⇢ user.code.Class.method(Class.java:123)"})

	 *
If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.

	 *
If the sanitized stack is empty, returns {@code "[no operator assembly information]"}

	 *

	 *
	 *
	 * @param source the sanitized assembly stacktrace in String format.
	 * @return a {@link String} representing operator and operator assembly site extracted
	 * from the assembly stack trace.
	 */
	static String extractOperatorAssemblyInformation(String source, boolean skipFirst) {
		String[] uncleanTraces = source.split("\n");
		final List<String> traces = Stream.of(uncleanTraces)
		                                  .map(String::trim)
		                                  .filter(s -> !s.isEmpty())
		                                  .skip(skipFirst ? 1 : 0)
		                                  .collect(Collectors.toList());

		if (traces.isEmpty()) {
			return "[no operator assembly information]";
		}

		int i = 0;
		while (i < traces.size() && !isUserCode(traces.get(i))) {
			i++;
		}

		String apiLine;
		String userCodeLine;
		if (i == 0) {
			//no line was a reactor API line
			apiLine = "";
			userCodeLine = traces.get(0);
		}
		else if (i == traces.size()) {
			//we skipped ALL lines, meaning they're all reactor API lines. We'll fully display the last one
			apiLine = "";
			userCodeLine = traces.get(i-1).replaceFirst("reactor.core.publisher.", "");
		}
		else {
			//currently on user code line, previous one is API
			apiLine = traces.get(i - 1);
			userCodeLine = traces.get(i);
		}

		//now we want something in the form "Flux.map ⇢ user.code.Class.method(Class.java:123)"
		if (apiLine.isEmpty()) return userCodeLine;

		int linePartIndex = apiLine.indexOf('(');
		if (linePartIndex > 0) {
			apiLine = apiLine.substring(0, linePartIndex);
		}
		apiLine = apiLine.replaceFirst("reactor.core.publisher.", "");

		return apiLine + " ⇢ " + userCodeLine;
	}
}


/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *       https://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 reactor.core.publisher;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Nullable;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

/**
 * Activate metrics gathering on a {@link Flux}, assuming Micrometer is on the classpath.
 *
 * @implNote Metrics.isInstrumentationAvailable() test should be performed BEFORE instantiating
 * or referencing this class, otherwise a {@link NoClassDefFoundError} will be thrown if
 * Micrometer is not there.
 *
 * @author Simon Baslé
 */
final class FluxMetrics<T> extends FluxOperator<T, T> {

	private static final Logger LOGGER = Loggers.getLogger(FluxMetrics.class);

	//=== Constants ===

	/**
	 * The default sequence name that will be used for instrumented {@link Flux} and
	 * {@link Mono} that don't have a {@link Flux#name(String) name}.
	 *
	 * @see #TAG_SEQUENCE_NAME
	 */
	static final String REACTOR_DEFAULT_NAME = "reactor";

	//Note: meters and tag names are normalized by micrometer on the basis that the word
	// separator is the dot, not camelCase...
	/**
	 * Meter that counts the number of events received from a malformed source (ie an
	 * onNext after an onComplete).
	 */
	static final String METER_MALFORMED     = "reactor.malformed.source";
	/**
	 * Meter that counts the number of subscriptions to a sequence.
	 */
	static final String METER_SUBSCRIBED    = "reactor.subscribed";
	/**
	 * Meter that times the duration between the subscription and the sequence's terminal
	 * event. The timer is also using the {@link #TAG_STATUS} tag to determine
	 * which kind of event terminated the sequence.
	 */
	static final String METER_FLOW_DURATION = "reactor.flow.duration";
	/**
	 * Meter that times the delays between each onNext (or between the first onNext and
	 * the onSubscribe event).
	 */
	static final String METER_ON_NEXT_DELAY = "reactor.onNext.delay";
	/**
	 * Meter that tracks the request amount, in {@link Flux#name(String) named}
	 * sequences only.
	 */
	static final String METER_REQUESTED     = "reactor.requested";

	/**
	 * Tag used by {@link #METER_FLOW_DURATION} to mark what kind of terminating
	 * event occurred: {@link #TAGVALUE_ON_COMPLETE}, {@link #TAGVALUE_ON_ERROR} or
	 * {@link #TAGVALUE_CANCEL}.
	 */
	static final String TAG_STATUS = "status";

	/**
	 * Tag used by {@link #METER_FLOW_DURATION} when {@link #TAG_STATUS}
	 * is {@link #TAGVALUE_ON_ERROR}, to store the exception that occurred.
	 */
	static final String TAG_EXCEPTION = "exception";

	/**
	 * Tag bearing the sequence's name, as given by the {@link Flux#name(String)} operator.
	 */
	static final String TAG_SEQUENCE_NAME    = "flow";
	/**
	 * Tag bearing the sequence's type, {@link Flux} or {@link Mono}.
	 * @see #TAGVALUE_FLUX
	 * @see #TAGVALUE_MONO
	 */
	static final String TAG_SEQUENCE_TYPE    = "type";

	//... tag values are free-for-all
	static final String TAGVALUE_ON_ERROR    = "error";
	static final String TAGVALUE_ON_COMPLETE = "completed";
	static final String TAGVALUE_CANCEL      = "cancelled";
	static final String TAGVALUE_FLUX        = "Flux";
	static final String TAGVALUE_MONO        = "Mono";

	// === Utility Methods ===

	/**
	 * Extract the name and tags from the upstream, and detect if there was an actual name
	 * (ie. distinct from {@link Scannable#stepName()}) set by the user.
	 *
	 * @param source the upstream
	 * @return a {@link Tuple2} of name and list of {@link Tag}
	 */
	static Tuple2<String, List<Tag>> resolveNameAndTags(Publisher<?> source) {
		//resolve the tags and names at instantiation
		String name;
		List<Tag> tags;

		Scannable scannable = Scannable.from(source);
		if (scannable.isScanAvailable()) {
			String nameOrDefault = scannable.name();
			if (scannable.stepName().equals(nameOrDefault)) {
				name = REACTOR_DEFAULT_NAME;
			}
			else {
				name = nameOrDefault;
			}
			tags = scannable.tags()
			                .map(tuple -> Tag.of(tuple.getT1(), tuple.getT2()))
			                .collect(Collectors.toList());
		}
		else {
			LOGGER.warn("Attempting to activate metrics but the upstream is not Scannable. " +
					"You might want to use `name()` (and optionally `tags()`) right before `metrics()`");
			name = REACTOR_DEFAULT_NAME;
			tags = Collections.emptyList();
		}

		return Tuples.of(name, tags);
	}

	// === Operator ===

	final String    name;
	final List<Tag> tags;
	final MeterRegistry    registryCandidate;

	FluxMetrics(Flux<? extends T> flux) {
		this(flux, null);
	}

	/**
	 * For testing purposes.
	 *
	 * @param registry the registry to use, or null for global one
	 */
	FluxMetrics(Flux<? extends T> flux, @Nullable MeterRegistry registry) {
		super(flux);

		Tuple2<String, List<Tag>> nameAndTags = resolveNameAndTags(flux);
		this.name = nameAndTags.getT1();
		this.tags = nameAndTags.getT2();

		if (registry == null) {
			this.registryCandidate = Metrics.globalRegistry;
		}
		else {
			this.registryCandidate = registry;
		}

	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		source.subscribe(new MicrometerFluxMetricsSubscriber<>(actual, registryCandidate,
				Clock.SYSTEM, this.name, this.tags));
	}

	static class MicrometerFluxMetricsSubscriber<T> implements InnerOperator<T,T> {

		final CoreSubscriber<? super T> actual;
		final Clock                     clock;

		final Counter             malformedSourceCounter;
		final Counter             subscribedCounter;
		final DistributionSummary requestedCounter;

		Timer.Sample subscribeToTerminateSample;
		long lastNextEventNanos = -1L;

		boolean done;
		@Nullable
		Fuseable.QueueSubscription<T> qs;
		Subscription s;

		final Timer         onNextIntervalTimer;
		final Timer         subscribeToCompleteTimer;
		final Function<Throwable, Timer> subscribeToErrorTimerFactory;
		final Timer         subscribeToCancelTimer;

		MicrometerFluxMetricsSubscriber(CoreSubscriber<? super T> actual,
				MeterRegistry registry,
				Clock clock,
				String sequenceName,
				List<Tag> sequenceTags) {
			this.actual = actual;
			this.clock = clock;

			List<Tag> commonTags = new ArrayList<>();
			commonTags.add(Tag.of(TAG_SEQUENCE_NAME, sequenceName));
			commonTags.add(Tag.of(TAG_SEQUENCE_TYPE, TAGVALUE_FLUX));
			commonTags.addAll(sequenceTags);

			this.subscribeToCompleteTimer = Timer
					.builder(METER_FLOW_DURATION)
					.tags(commonTags)
					.tag(TAG_STATUS, TAGVALUE_ON_COMPLETE)
					.tag(TAG_EXCEPTION, "")
					.description("Times the duration elapsed between a subscription and the onComplete termination of the sequence")
					.register(registry);
			this.subscribeToCancelTimer = Timer
					.builder(METER_FLOW_DURATION)
					.tags(commonTags)
					.tag(TAG_STATUS, TAGVALUE_CANCEL)
					.tag(TAG_EXCEPTION, "")
					.description("Times the duration elapsed between a subscription and the cancellation of the sequence")
					.register(registry);

			//note that Builder ISN'T TRULY IMMUTABLE. This is ok though as there will only ever be one usage.
			Timer.Builder subscribeToErrorTimerBuilder = Timer
					.builder(METER_FLOW_DURATION)
					.tags(commonTags)
					.tag(TAG_STATUS, TAGVALUE_ON_ERROR)
					.description("Times the duration elapsed between a subscription and the onError termination of the sequence, with the exception name as a tag");
			this.subscribeToErrorTimerFactory = e -> {
				return subscribeToErrorTimerBuilder
						.tag(TAG_EXCEPTION, e.getClass().getName())
						.register(registry);
			};

			this.onNextIntervalTimer = Timer
					.builder(METER_ON_NEXT_DELAY)
					.tags(commonTags)
					.description("Measures delays between onNext signals (or between onSubscribe and first onNext)")
					.register(registry);

			this.subscribedCounter = Counter
					.builder(METER_SUBSCRIBED)
					.tags(commonTags)
					.baseUnit("subscribers")
					.description("Counts how many Reactor sequences have been subscribed to")
					.register(registry);

			this.malformedSourceCounter = registry.counter(METER_MALFORMED, commonTags);

			if (!REACTOR_DEFAULT_NAME.equals(sequenceName)) {
				this.requestedCounter = DistributionSummary
						.builder(METER_REQUESTED)
						.tags(commonTags)
						.description("Counts the amount requested to a named Flux by all subscribers, until at least one requests an unbounded amount")
						.baseUnit("requested amount")
						.register(registry);
			}
			else {
				requestedCounter = null;
			}
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return actual;
		}

		@Override
		public void onNext(T t) {
			if (done) {
				this.malformedSourceCounter.increment();
				Operators.onNextDropped(t, actual.currentContext());
				return;
			}

			//record the delay since previous onNext/onSubscribe. This also records the count.
			long last = this.lastNextEventNanos;
			this.lastNextEventNanos = clock.monotonicTime();
			this.onNextIntervalTimer.record(lastNextEventNanos - last, TimeUnit.NANOSECONDS);

			actual.onNext(t);
		}

		@Override
		public void onError(Throwable e) {
			if (done) {
				this.malformedSourceCounter.increment();
				Operators.onErrorDropped(e, actual.currentContext());
				return;
			}
			done = true;
			//we don't record the time between last onNext and onError,
			// because it would skew the onNext count by one

			//register a timer for that particular exception
			Timer timer = subscribeToErrorTimerFactory.apply(e);
			//record error termination
			this.subscribeToTerminateSample.stop(timer);

			actual.onError(e);
		}

		@Override
		public void onComplete() {
			if (done) {
				this.malformedSourceCounter.increment();
				return;
			}
			done = true;
			//we don't record the time between last onNext and onComplete,
			// because it would skew the onNext count by one
			this.subscribeToTerminateSample.stop(subscribeToCompleteTimer);

			actual.onComplete();
		}

		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.validate(this.s, s)) {
				this.subscribedCounter.increment();
				this.subscribeToTerminateSample = Timer.start(clock);
				this.lastNextEventNanos = clock.monotonicTime();

				if (s instanceof Fuseable.QueueSubscription) {
					//noinspection unchecked
					this.qs = (Fuseable.QueueSubscription<T>) s;
				}
				this.s = s;
				actual.onSubscribe(this);

			}
		}

		@Override
		public void request(long l) {
			if (Operators.validate(l)) {
				if (requestedCounter != null) {
					requestedCounter.record(l);
				}
				s.request(l);
			}
		}

		@Override
		public void cancel() {
			//we don't record the time between last onNext and cancel,
			// because it would skew the onNext count by one
			this.subscribeToTerminateSample.stop(subscribeToCancelTimer);

			s.cancel();
		}
	}

	/**
	 * @implNote we don't want to particularly track fusion-specific calls, as this
	 * subscriber would only detect fused subsequences immediately upstream of it, so
	 * Counters would be a bit irrelevant. We however want to instrument onNext counts.
	 *
	 * @param <T>
	 */
	static final class MicrometerFluxMetricsFuseableSubscriber<T> extends MicrometerFluxMetricsSubscriber<T>
			implements Fuseable, Fuseable.QueueSubscription<T> {

		private int fusionMode;

		MicrometerFluxMetricsFuseableSubscriber(CoreSubscriber<? super T> actual,
				MeterRegistry registry, Clock clock, String sequenceName, List<Tag> sequenceTags) {
			super(actual, registry, clock, sequenceName, sequenceTags);
		}

		@Override
		public void onNext(T t) {
			if (this.fusionMode == Fuseable.ASYNC) {
				actual.onNext(null);
				return;
			}

			if (done) {
				this.malformedSourceCounter.increment();
				Operators.onNextDropped(t, actual.currentContext());
				return;
			}

			//record the delay since previous onNext/onSubscribe. This also records the count.
			long last = this.lastNextEventNanos;
			this.lastNextEventNanos = clock.monotonicTime();
			this.onNextIntervalTimer.record(lastNextEventNanos - last, TimeUnit.NANOSECONDS);

			actual.onNext(t);
		}

		@Override
		public int requestFusion(int mode) {
			//Simply negotiate the fusion by delegating:
			if (qs != null) {
				this.fusionMode = qs.requestFusion(mode);
				return fusionMode;
			}
			return Fuseable.NONE; //should not happen unless requestFusion called before subscribe
		}

		@Override
		@Nullable
		public T poll() {
			if (qs == null) {
				return null;
			}
			try {
				T v = qs.poll();

				if (v == null && fusionMode == SYNC) {
					//this is also a complete event
					this.subscribeToTerminateSample.stop(subscribeToCompleteTimer);
				}
				if (v != null) {
					//this is an onNext event
					//record the delay since previous onNext/onSubscribe. This also records the count.
					long last = this.lastNextEventNanos;
					this.lastNextEventNanos = clock.monotonicTime();
					this.onNextIntervalTimer.record(lastNextEventNanos - last, TimeUnit.NANOSECONDS);
				}
				return v;
			} catch (Throwable e) {
				//register a timer for that particular exception
				Timer timer = subscribeToErrorTimerFactory.apply(e);
				//record error termination
				this.subscribeToTerminateSample.stop(timer);
				throw e;
			}
		}

		@Override
		public void clear() {
			if (qs != null) {
				qs.clear();
			}
		}

		@Override
		public boolean isEmpty() {
			return qs == null || qs.isEmpty();
		}

		@Override
		public int size() {
			return qs == null ? 0 : qs.size();
		}
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *       https://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 reactor.util.function;

import java.util.Objects;
import java.util.function.Function;

import reactor.util.annotation.NonNull;
import reactor.util.annotation.Nullable;

/**
 * A tuple that holds six values
 *
 * @param <T1> The type of the first value held by this tuple
 * @param <T2> The type of the second value held by this tuple
 * @param <T3> The type of the third value held by this tuple
 * @param <T4> The type of the fourth value held by this tuple
 * @param <T5> The type of the fifth value held by this tuple
 * @param <T6> The type of the sixth value held by this tuple
 * @author Jon Brisbin
 * @author Stephane Maldini
 */
public class Tuple6<T1, T2, T3, T4, T5, T6> extends Tuple5<T1, T2, T3, T4, T5> {

	private static final long serialVersionUID = 770306356087176830L;

	@NonNull final T6 t6;

	Tuple6(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
		super(t1, t2, t3, t4, t5);
		this.t6 = Objects.requireNonNull(t6, "t6");
	}

	/**
	 * Type-safe way to get the sixth object of this {@link Tuples}.
	 *
	 * @return The sixth object
	 */
	public T6 getT6() {
		return t6;
	}

	/**
	 * Map the 1st part (T1) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T1 part
	 * @param <R> the new type for the T1 part
	 * @return a new {@link Tuple6} with a different T1 value
	 */
	public <R> Tuple6<R, T2, T3, T4, T5, T6> mapT1(Function<T1, R> mapper) {
		return new Tuple6<>(mapper.apply(t1), t2, t3, t4, t5, t6);
	}

	/**
	 * Map the 2nd part (T2) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T2 part
	 * @param <R> the new type for the T2 part
	 * @return a new {@link Tuple6} with a different T2 value
	 */
	public <R> Tuple6<T1, R, T3, T4, T5, T6> mapT2(Function<T2, R> mapper) {
		return new Tuple6<>(t1, mapper.apply(t2), t3, t4, t5, t6);
	}

	/**
	 * Map the 3rd part (T3) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T3 part
	 * @param <R> the new type for the T3 part
	 * @return a new {@link Tuple6} with a different T3 value
	 */
	public <R> Tuple6<T1, T2, R, T4, T5, T6> mapT3(Function<T3, R> mapper) {
		return new Tuple6<>(t1, t2, mapper.apply(t3), t4, t5, t6);
	}

	/**
	 * Map the 4th part (T4) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T4 part
	 * @param <R> the new type for the T4 part
	 * @return a new {@link Tuple6} with a different T4 value
	 */
	public <R> Tuple6<T1, T2, T3, R, T5, T6> mapT4(Function<T4, R> mapper) {
		return new Tuple6<>(t1, t2, t3, mapper.apply(t4), t5, t6);
	}

	/**
	 * Map the 5th part (T5) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T5 part
	 * @param <R> the new type for the T5 part
	 * @return a new {@link Tuple6} with a different T5 value
	 */
	public <R> Tuple6<T1, T2, T3, T4, R, T6> mapT5(Function<T5, R> mapper) {
		return new Tuple6<>(t1, t2, t3, t4, mapper.apply(t5), t6);
	}

	/**
	 * Map the 6th part (T6) of this {@link Tuple6} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T6 part
	 * @param <R> the new type for the T6 part
	 * @return a new {@link Tuple6} with a different T6 value
	 */
	public <R> Tuple6<T1, T2, T3, T4, T5, R> mapT6(Function<T6, R> mapper) {
		return new Tuple6<>(t1, t2, t3, t4, t5, mapper.apply(t6));
	}

	@Nullable
	@Override
	public Object get(int index) {
		switch (index) {
			case 0:
				return t1;
			case 1:
				return t2;
			case 2:
				return t3;
			case 3:
				return t4;
			case 4:
				return t5;
			case 5:
				return t6;
			default:
				return null;
		}
	}

	@Override
	public Object[] toArray() {
		return new Object[]{t1, t2, t3, t4, t5, t6};
	}

	@Override
	public boolean equals(@Nullable Object o) {
		if (this == o) return true;
		if (!(o instanceof Tuple6)) return false;
		if (!super.equals(o)) return false;

		@SuppressWarnings("rawtypes")
        Tuple6 tuple6 = (Tuple6) o;

		return t6.equals(tuple6.t6);

	}

	@Override
	public int hashCode() {
		int result = super.hashCode();
		result = 31 * result + t6.hashCode();
		return result;
	}

	@Override
	public int size() {
		return 6;
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *       https://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 reactor.core;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import reactor.util.annotation.Nullable;

/**
 * Global Reactor Core Exception handling and utils to operate on.
 *
 * @author Stephane Maldini
 * @see Reactive-Streams-Commons
 */
public abstract class Exceptions {

	/**
	 * A common error message used when a reactive streams source doesn't seem to respect
	 * backpressure signals, resulting in an operator's internal queue to be full.
	 */
	public static final String BACKPRESSURE_ERROR_QUEUE_FULL = "Queue is full: Reactive Streams source doesn't respect backpressure";

	/**
	 * A singleton instance of a Throwable indicating a terminal state for exceptions,
	 * don't leak this!
	 */
	@SuppressWarnings("ThrowableInstanceNeverThrown")
	public static final Throwable TERMINATED = new Throwable("Operator has been terminated");

	/**
	 * Update an empty atomic reference with the given exception, or combine further added
	 * exceptions together as suppressed exceptions under a root Throwable with
	 * the {@code "Multiple exceptions"} message, if the atomic reference already holds
	 * one. This is short-circuited if the reference contains {@link #TERMINATED}.
	 *
	 * @param <T> the parent instance type
	 * @param field the target field updater
	 * @param instance the parent instance for the field
	 * @param exception the Throwable to add.
	 *
	 * @return true if added, false if the field contained the {@link #TERMINATED}
	 * instance.
	 * @see #unwrapMultiple(Throwable)
	 */
	public static <T> boolean addThrowable(AtomicReferenceFieldUpdater<T, Throwable> field,
			T instance,
			Throwable exception) {
		for (; ; ) {
			Throwable current = field.get(instance);

			if (current == TERMINATED) {
				return false;
			}

			if (current instanceof CompositeException) {
				//this is ok, composite exceptions are never singletons
				current.addSuppressed(exception);
				return true;
			}

			Throwable update;
			if (current == null) {
				update = exception;
			} else {
				update = multiple(current, exception);
			}

			if (field.compareAndSet(instance, current, update)) {
				return true;
			}
		}
	}

	/**
	 * Create a composite exception that wraps the given {@link Throwable Throwable(s)},
	 * as suppressed exceptions. Instances create by this method can be detected using the
	 * {@link #isMultiple(Throwable)} check. The {@link #unwrapMultiple(Throwable)} method
	 * will correctly unwrap these to a {@link List} of the suppressed exceptions. Note
	 * that is will also be consistent in producing a List for other types of exceptions
	 * by putting the input inside a single-element List.
	 *
	 * @param throwables the exceptions to wrap into a composite
	 * @return a composite exception with a standard message, and the given throwables as
	 * suppressed exceptions
	 * @see #addThrowable(AtomicReferenceFieldUpdater, Object, Throwable)
	 */
	public static RuntimeException multiple(Throwable... throwables) {
		CompositeException multiple = new CompositeException();
		//noinspection ConstantConditions
		if (throwables != null) {
			for (Throwable t : throwables) {
				//this is ok, multiple is always a new non-singleton instance
				multiple.addSuppressed(t);
			}
		}
		return multiple;
	}

	/**
	 * Create a composite exception that wraps the given {@link Throwable Throwable(s)},
	 * as suppressed exceptions. Instances create by this method can be detected using the
	 * {@link #isMultiple(Throwable)} check. The {@link #unwrapMultiple(Throwable)} method
	 * will correctly unwrap these to a {@link List} of the suppressed exceptions. Note
	 * that is will also be consistent in producing a List for other types of exceptions
	 * by putting the input inside a single-element List.
	 *
	 * @param throwables the exceptions to wrap into a composite
	 * @return a composite exception with a standard message, and the given throwables as
	 * suppressed exceptions
	 * @see #addThrowable(AtomicReferenceFieldUpdater, Object, Throwable)
	 */
	public static RuntimeException multiple(Iterable<Throwable> throwables) {
		RuntimeException multiple = new RuntimeException("Multiple exceptions");
		//noinspection ConstantConditions
		if (throwables != null) {
			for (Throwable t : throwables) {
				//this is ok, multiple is always a new non-singleton instance
				multiple.addSuppressed(t);
			}
		}
		return multiple;
	}

	/**
	 * Prepare an unchecked {@link RuntimeException} that will bubble upstream if thrown
	 * by an operator.
This method invokes {@link #throwIfFatal(Throwable)}.
	 *
	 * @param t the root cause
	 *
	 * @return an unchecked exception that should choose bubbling up over error callback
	 * path
	 */
	public static RuntimeException bubble(Throwable t) {
		throwIfFatal(t);
		return new BubblingException(t);
	}

	/**
	 * @return a new {@link IllegalStateException} with a cause message abiding to
	 * reactive stream specification rule 2.12.
	 */
	public static IllegalStateException duplicateOnSubscribeException() {
		return new IllegalStateException(
				"Spec. Rule 2.12 - Subscriber.onSubscribe MUST NOT be called more than once (based on object equality)");
	}

	/**
	 * Return an {@link UnsupportedOperationException} indicating that the error callback
	 * on a subscriber was not implemented, yet an error was propagated.
	 *
	 * @param cause original error not processed by a receiver.
	 * @return an {@link UnsupportedOperationException} indicating the error callback was
	 * not implemented and holding the original propagated error.
	 * @see #isErrorCallbackNotImplemented(Throwable)
	 */
	public static UnsupportedOperationException errorCallbackNotImplemented(Throwable cause) {
		Objects.requireNonNull(cause, "cause");
		return new ErrorCallbackNotImplemented(cause);
	}

	/**
	 * An exception that is propagated upward and considered as "fatal" as per Reactive
	 * Stream limited list of exceptions allowed to bubble. It is not meant to be common
	 * error resolution but might assist implementors in dealing with boundaries (queues,
	 * combinations and async).
	 *
	 * @return a {@link RuntimeException} that can be identified via {@link #isCancel}
	 * @see #isCancel(Throwable)
	 */
	public static RuntimeException failWithCancel() {
		return new CancelException();
	}

	/**
	 * Return an {@link IllegalStateException} indicating the receiver is overrun by
	 * more signals than expected in case of a bounded queue, or more generally that data
	 * couldn't be emitted due to a lack of request
	 *
	 * @return an {@link IllegalStateException}
	 * @see #isOverflow(Throwable)
	 */
	public static IllegalStateException failWithOverflow() {
		return new OverflowException("The receiver is overrun by more signals than expected (bounded queue...)");
	}

	/**
	 * Return an {@link IllegalStateException} indicating the receiver is overrun by
	 * more signals than expected in case of a bounded queue or more generally that data
	 * couldn't be emitted due to a lack of request
	 *
	 * @param message the exception's message
	 * @return an {@link IllegalStateException}
	 * @see #isOverflow(Throwable)
	 */
	public static IllegalStateException failWithOverflow(String message) {
		return new OverflowException(message);
	}

	/**
	 * Return a singleton {@link RejectedExecutionException}
	 *
	 * @return a singleton {@link RejectedExecutionException}
	 */
	public static RejectedExecutionException failWithRejected() {
		return REJECTED_EXECUTION;
	}

	/**
	 * Return a singleton {@link RejectedExecutionException} with a message indicating
	 * the reason is due to the scheduler not being time-capable
	 *
	 * @return a singleton {@link RejectedExecutionException}
	 */
	public static RejectedExecutionException failWithRejectedNotTimeCapable() {
		return NOT_TIME_CAPABLE_REJECTED_EXECUTION;
	}

	/**
	 * Return a new {@link RejectedExecutionException} with standard message and cause,
	 * unless the {@code cause} is already a {@link RejectedExecutionException} created
	 * via {@link #failWithRejected(Throwable)} (not the singleton-producing variants).
	 *
	 * @param cause the original exception that caused the rejection
	 * @return a new {@link RejectedExecutionException} with standard message and cause
	 */
	public static RejectedExecutionException failWithRejected(Throwable cause) {
		if (cause instanceof ReactorRejectedExecutionException) {
			return (RejectedExecutionException) cause;
		}
		return new ReactorRejectedExecutionException("Scheduler unavailable", cause);
	}

	/**
	 * Check if the given exception represents an {@link #failWithOverflow() overflow}.
	 * @param t the {@link Throwable} error to check
	 * @return true if the given {@link Throwable} represents an overflow.
	 */
	public static boolean isOverflow(@Nullable Throwable t) {
		return t instanceof OverflowException;
	}

	/**
	 * Check if the given exception is a {@link #bubble(Throwable) bubbled} wrapped exception.
	 * @param t the {@link Throwable} error to check
	 * @return true if given {@link Throwable} is a bubbled wrapped exception.
	 */
	public static boolean isBubbling(@Nullable Throwable t) {
		return t instanceof BubblingException;
	}

	/**
	 * Check if the given error is a {@link #failWithCancel() cancel signal}.
	 * @param t the {@link Throwable} error to check
	 * @return true if given {@link Throwable} is a cancellation token.
	 */
	public static boolean isCancel(@Nullable Throwable t) {
		return t instanceof CancelException;
	}

	/**
	 * Check if the given error is a {@link #errorCallbackNotImplemented(Throwable) callback not implemented}
	 * exception, in which case its {@link Throwable#getCause() cause} will be the propagated
	 * error that couldn't be processed.
	 *
	 * @param t the {@link Throwable} error to check
	 * @return true if given {@link Throwable} is a callback not implemented exception.
	 */
	public static boolean isErrorCallbackNotImplemented(@Nullable Throwable t) {
		return t != null && t.getClass().equals(ErrorCallbackNotImplemented.class);
	}

	/**
	 * Check a {@link Throwable} to see if it is a composite, as created by {@link #multiple(Throwable...)}.
	 *
	 * @param t the {@link Throwable} to check, {@literal null} always yields {@literal false}
	 * @return true if the Throwable is an instance created by {@link #multiple(Throwable...)}, false otherwise
	 */
	public static boolean isMultiple(@Nullable Throwable t) {
		return t instanceof CompositeException;
	}

	/**
	 * @param elements the invalid requested demand
	 *
	 * @return a new {@link IllegalArgumentException} with a cause message abiding to
	 * reactive stream specification rule 3.9.
	 */
	public static IllegalArgumentException nullOrNegativeRequestException(long elements) {
		return new IllegalArgumentException(
				"Spec. Rule 3.9 - Cannot request a non strictly positive number: " + elements);
	}

	/**
	 * Prepare an unchecked {@link RuntimeException} that should be propagated
	 * downstream through {@link org.reactivestreams.Subscriber#onError(Throwable)}.
	 *
This method invokes {@link #throwIfFatal(Throwable)}.
	 *
	 * @param t the root cause
	 *
	 * @return an unchecked exception to propagate through onError signals.
	 */
	public static RuntimeException propagate(Throwable t) {
		throwIfFatal(t);
		if (t instanceof RuntimeException) {
			return (RuntimeException) t;
		}
		return new ReactiveException(t);
	}

	/**
	 * Atomic utility to safely mark a volatile throwable reference with a terminal
	 * marker.
	 *
	 * @param field the atomic container
	 * @param instance the reference instance
	 * @param <T> the instance type
	 *
	 * @return the previously masked throwable
	 */
	@Nullable
	public static <T> Throwable terminate(AtomicReferenceFieldUpdater<T, Throwable> field,
			T instance) {
		Throwable current = field.get(instance);
		if (current != TERMINATED) {
			current = field.getAndSet(instance, TERMINATED);
		}
		return current;
	}

	/**
	 * Throws a particular {@code Throwable} only if it belongs to a set of "fatal" error
	 * varieties. These varieties are as follows:

	 *
{@code BubblingException} (as detectable by {@link #isBubbling(Throwable)})

	 *
{@code ErrorCallbackNotImplemented} (as detectable by {@link #isErrorCallbackNotImplemented(Throwable)})

	 *
{@link VirtualMachineError}

{@link ThreadDeath}

{@link LinkageError}

	 *
	 * @param t the exception to evaluate
	 */
	public static void throwIfFatal(@Nullable Throwable t) {
		if (t instanceof BubblingException) {
			throw (BubblingException) t;
		}
		if (t instanceof ErrorCallbackNotImplemented) {
			throw (ErrorCallbackNotImplemented) t;
		}
		throwIfJvmFatal(t);
	}

	/**
	 * Throws a particular {@code Throwable} only if it belongs to a set of "fatal" error
	 * varieties native to the JVM. These varieties are as follows:
	 *

{@link VirtualMachineError}

{@link ThreadDeath}

	 *
{@link LinkageError}

	 *
	 * @param t the exception to evaluate
	 */
	public static void throwIfJvmFatal(@Nullable Throwable t) {
		if (t instanceof VirtualMachineError) {
			throw (VirtualMachineError) t;
		}
		if (t instanceof ThreadDeath) {
			throw (ThreadDeath) t;
		}
		if (t instanceof LinkageError) {
			throw (LinkageError) t;
		}
	}

	/**
	 * Unwrap a particular {@code Throwable} only if it is was wrapped via
	 * {@link #bubble(Throwable) bubble} or {@link #propagate(Throwable) propagate}.
	 *
	 * @param t the exception to unwrap
	 *
	 * @return the unwrapped exception or current one if null
	 */
	public static Throwable unwrap(Throwable t) {
		Throwable _t = t;
		while (_t instanceof ReactiveException) {
			_t = _t.getCause();
		}
		return _t == null ? t : _t;
	}

	/**
	 * Attempt to unwrap a {@link Throwable} into a {@link List} of Throwables. This is
	 * only done on the condition that said Throwable is a composite exception built by
	 * {@link #multiple(Throwable...)}, in which case the list contains the exceptions
	 * wrapped as suppressed exceptions in the composite. In any other case, the list
	 * only contains the input Throwable (or is empty in case of null input).
	 *
	 * @param potentialMultiple the {@link Throwable} to unwrap if multiple
	 * @return a {@link List} of the exceptions suppressed by the {@link Throwable} if
	 * multiple, or a List containing the Throwable otherwise. Null input results in an
	 * empty List.
	 */
	public static List<Throwable> unwrapMultiple(@Nullable Throwable potentialMultiple) {
		if (potentialMultiple == null) {
			return Collections.emptyList();
		}

		if (isMultiple(potentialMultiple)) {
			return Arrays.asList(potentialMultiple.getSuppressed());
		}

		return Collections.singletonList(potentialMultiple);
	}

	/**
	 * Safely suppress a {@link Throwable} on a {@link RuntimeException}. The returned
	 * {@link RuntimeException}, bearing the suppressed exception, is most often the same
	 * as the original exception unless:
	 *

	 *
original and tentatively suppressed exceptions are one and the same: do
	 *     nothing but return the original.

	 *
original is one of the singleton {@link RejectedExecutionException} created
	 *     by Reactor: make a copy the {@link RejectedExecutionException}, add the
	 *     suppressed exception to it and return that copy.

	 *
	 *

	 * @param original the original {@link RuntimeException} to bear a suppressed exception
	 * @param suppressed the {@link Throwable} to suppress
	 * @return the (most of the time original) {@link RuntimeException} bearing the
	 * suppressed {@link Throwable}
	 */
	public static final RuntimeException addSuppressed(RuntimeException original, Throwable suppressed) {
		if (original == suppressed) {
			return original;
		}
		if (original == REJECTED_EXECUTION || original == NOT_TIME_CAPABLE_REJECTED_EXECUTION) {
			RejectedExecutionException ree = new RejectedExecutionException(original.getMessage());
			ree.addSuppressed(suppressed);
			return ree;
		}
		else {
			original.addSuppressed(suppressed);
			return original;
		}
	}

	/**
	 * Safely suppress a {@link Throwable} on an original {@link Throwable}. The returned
	 * {@link Throwable}, bearing the suppressed exception, is most often the same
	 * as the original one unless:
	 *

	 *
original and tentatively suppressed exceptions are one and the same: do
	 *     nothing but return the original.

	 *
original is one of the singleton {@link RejectedExecutionException} created
	 *     by Reactor: make a copy the {@link RejectedExecutionException}, add the
	 *     suppressed exception to it and return that copy.

	 *
original is a special internal TERMINATED singleton instance: return it
	 *     without suppressing the exception.

	 *
	 *

	 * @param original the original {@link Throwable} to bear a suppressed exception
	 * @param suppressed the {@link Throwable} to suppress
	 * @return the (most of the time original) {@link Throwable} bearing the
	 * suppressed {@link Throwable}
	 */
	public static final Throwable addSuppressed(Throwable original, final Throwable suppressed) {
		if (original == suppressed) {
			return original;
		}

		if (original == TERMINATED) {
			return original;
		}

		if (original == REJECTED_EXECUTION || original == NOT_TIME_CAPABLE_REJECTED_EXECUTION) {
			RejectedExecutionException ree = new RejectedExecutionException(original.getMessage());
			ree.addSuppressed(suppressed);
			return ree;
		}
		else {
			original.addSuppressed(suppressed);
			return original;
		}
	}

	Exceptions() {
	}

	static final RejectedExecutionException REJECTED_EXECUTION = new RejectedExecutionException("Scheduler unavailable");

	static final RejectedExecutionException NOT_TIME_CAPABLE_REJECTED_EXECUTION =
			new RejectedExecutionException(
					"Scheduler is not capable of time-based scheduling");

	static class CompositeException extends ReactiveException {

		CompositeException() {
			super("Multiple exceptions");
		}

		private static final long serialVersionUID = 8070744939537687606L;
	}

	static class BubblingException extends ReactiveException {

		BubblingException(String message) {
			super(message);
		}

		BubblingException(Throwable cause) {
			super(cause);
		}

		private static final long serialVersionUID = 2491425277432776142L;
	}

	/**
	 * An exception that is propagated downward through {@link org.reactivestreams.Subscriber#onError(Throwable)}
	 */
	static class ReactiveException extends RuntimeException {

		ReactiveException(Throwable cause) {
			super(cause);
		}

		ReactiveException(String message) {
			super(message);
		}

		@Override
		public synchronized Throwable fillInStackTrace() {
			return getCause() != null ? getCause().fillInStackTrace() :
					super.fillInStackTrace();
		}

		private static final long serialVersionUID = 2491425227432776143L;
	}

	static final class ErrorCallbackNotImplemented extends UnsupportedOperationException {

		ErrorCallbackNotImplemented(Throwable cause) {
			super(cause);
		}

		@Override
		public synchronized Throwable fillInStackTrace() {
			return this;
		}

		private static final long serialVersionUID = 2491425227432776143L;
	}

	/**
	 * An error signal from downstream subscribers consuming data when their state is
	 * denying any additional event.
	 *
	 * @author Stephane Maldini
	 */
	static final class CancelException extends BubblingException {

		CancelException() {
			super("The subscriber has denied dispatching");
		}

		private static final long serialVersionUID = 2491425227432776144L;

	}

	static final class OverflowException extends IllegalStateException {

		OverflowException(String s) {
			super(s);
		}
	}

	static final class ReactorRejectedExecutionException extends RejectedExecutionException {

		ReactorRejectedExecutionException(String message, Throwable cause) {
			super(message, cause);
		}
	}

}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
 *
 * 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
 *
 *       https://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 reactor.core.publisher;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer;
import java.util.logging.Level;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;

import reactor.core.Fuseable;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

/**
 * A logging interceptor that intercepts all reactive calls and trace them.
 * The logging level can be tuned using {@link Level}, but only FINEST, FINE, INFO,
 * WARNING and SEVERE are taken into account.
 *
 * @author Stephane Maldini
 */
final class SignalLogger<IN> implements SignalPeek<IN> {

	final static int CONTEXT_PARENT    = 0b0100000000;
	final static int SUBSCRIBE         = 0b0010000000;
	final static int ON_SUBSCRIBE      = 0b0001000000;
	final static int ON_NEXT           = 0b0000100000;
	final static int ON_ERROR          = 0b0000010000;
	final static int ON_COMPLETE       = 0b0000001000;
	final static int REQUEST           = 0b0000000100;
	final static int CANCEL            = 0b0000000010;
	final static int AFTER_TERMINATE   = 0b0000000001;
	final static int ALL               =
			CONTEXT_PARENT | CANCEL | ON_COMPLETE | ON_ERROR | REQUEST | ON_SUBSCRIBE | ON_NEXT | SUBSCRIBE;

	final static AtomicLong IDS = new AtomicLong(1);

	final Publisher<IN> source;

	final Logger  log;
	final boolean fuseable;
	final int     options;
	final Level   level;
	final String  operatorLine;
	final long    id;

	static final String LOG_TEMPLATE          = "{}({})";
	static final String LOG_TEMPLATE_FUSEABLE = "| {}({})";

	SignalLogger(Publisher<IN> source,
			@Nullable String category,
			Level level,
			boolean correlateStack,
			SignalType... options) {
		this(source, category, level, correlateStack, Loggers::getLogger, options);
	}

	SignalLogger(Publisher<IN> source,
			@Nullable String category,
			Level level,
			boolean correlateStack,
			Function<String, Logger> loggerSupplier,
			@Nullable SignalType... options) {

		this.source = Objects.requireNonNull(source, "source");
		this.id = IDS.getAndIncrement();
		this.fuseable = source instanceof Fuseable;

		if (correlateStack) {
			operatorLine = Traces.extractOperatorAssemblyInformation(Traces.callSiteSupplierFactory.get().get());
		}
		else {
			operatorLine = null;
		}

		boolean generated =
				category == null || category.isEmpty() || category.endsWith(".");

		category = generated && category == null ? "reactor." : category;
		if (generated) {
			if (source instanceof Mono) {
				category += "Mono." + source.getClass()
				                            .getSimpleName()
				                            .replace("Mono", "");
			}
			else if (source instanceof ParallelFlux) {
				category += "Parallel." + source.getClass()
				                                .getSimpleName()
				                                .replace("Parallel", "");
			}
			else {
				category += "Flux." + source.getClass()
				                            .getSimpleName()
				                            .replace("Flux", "");
			}
			category += "." + id;
		}

		this.log = loggerSupplier.apply(category);

		this.level = level;
		if (options == null || options.length == 0) {
			this.options = ALL;
		}
		else {
			int opts = 0;
			for (SignalType option : options) {
				if (option == SignalType.CANCEL) {
					opts |= CANCEL;
				}
				else if (option == SignalType.CURRENT_CONTEXT) {
					opts |= CONTEXT_PARENT;
				}
				else if (option == SignalType.ON_SUBSCRIBE) {
					opts |= ON_SUBSCRIBE;
				}
				else if (option == SignalType.REQUEST) {
					opts |= REQUEST;
				}
				else if (option == SignalType.ON_NEXT) {
					opts |= ON_NEXT;
				}
				else if (option == SignalType.ON_ERROR) {
					opts |= ON_ERROR;
				}
				else if (option == SignalType.ON_COMPLETE) {
					opts |= ON_COMPLETE;
				}
				else if (option == SignalType.SUBSCRIBE) {
					opts |= SUBSCRIBE;
				}
				else if (option == SignalType.AFTER_TERMINATE) {
					opts |= AFTER_TERMINATE;
				}
			}
			this.options = opts;
		}
	}

	@Override
	@Nullable
	public Object scanUnsafe(Attr key) {
		if (key == Attr.PARENT) return source;

		return null;
	}

	/**
	 * Structured logging with level adaptation and operator ascii graph if required.
	 *
	 * @param signalType the type of signal being logged
	 * @param signalValue the value for the signal (use empty string if not required)
	 */
	void log(SignalType signalType, Object signalValue) {
		String line = fuseable ? LOG_TEMPLATE_FUSEABLE : LOG_TEMPLATE;
		if (operatorLine != null) {
			line = line + " " + operatorLine;
		}
		if (level == Level.FINEST) {
			log.trace(line, signalType, signalValue);
		}
		else if (level == Level.FINE) {
			log.debug(line, signalType, signalValue);
		}
		else if (level == Level.INFO) {
			log.info(line, signalType, signalValue);
		}
		else if (level == Level.WARNING) {
			log.warn(line, signalType, signalValue);
		}
		else if (level == Level.SEVERE) {
			log.error(line, signalType, signalValue);
		}
	}

	/**
	 * Structured logging with level adaptation and operator ascii graph if required +
	 * protection against loggers that detect objects like {@link Fuseable.QueueSubscription}
	 * as {@link java.util.Collection} and attempt to use their iterator for logging.
	 *
	 * @see #log
	 */
	void safeLog(SignalType signalType, Object signalValue) {
		if (signalValue instanceof Fuseable.QueueSubscription) {
			signalValue = String.valueOf(signalValue);
			if (log.isDebugEnabled()) {
				log.debug("A Fuseable Subscription has been passed to the logging framework, this is generally a sign of a misplaced log(), " +
						"eg. 'window(2).log()' instead of 'window(2).flatMap(w -> w.log())'");
			}
		}
		try {
			log(signalType, signalValue);
		}
		catch (UnsupportedOperationException uoe) {
			log(signalType, String.valueOf(signalValue));
			if (log.isDebugEnabled()) {
				log.debug("UnsupportedOperationException has been raised by the logging framework, does your log() placement make sense? " +
						"eg. 'window(2).log()' instead of 'window(2).flatMap(w -> w.log())'", uoe);
			}
		}
	}

	static String subscriptionAsString(@Nullable Subscription s) {
		if (s == null) {
			return "null subscription";
		}
		StringBuilder asString = new StringBuilder();
		if (s instanceof Fuseable.SynchronousSubscription) {
			asString.append("[Synchronous Fuseable] ");
		}
		else if (s instanceof Fuseable.QueueSubscription) {
			asString.append("[Fuseable] ");
		}

		Class<? extends Subscription> clazz = s.getClass();
		String name = clazz.getCanonicalName();
		if (name == null) {
			name = clazz.getName();
		}
		name = name.replaceFirst(clazz.getPackage()
		                              .getName() + ".", "");
		asString.append(name);

		return asString.toString();
	}

	@Override
	@Nullable
	public Consumer<? super Subscription> onSubscribeCall() {
		if ((options & ON_SUBSCRIBE) == ON_SUBSCRIBE && (level != Level.INFO || log.isInfoEnabled())) {
			return s -> log(SignalType.ON_SUBSCRIBE, subscriptionAsString(s));
		}
		return null;
	}

	@Nullable
	@Override
	public Consumer<? super Context> onCurrentContextCall() {
		if ((options & CONTEXT_PARENT) == CONTEXT_PARENT && (level != Level.INFO || log
				.isInfoEnabled())) {
			return c -> log(SignalType.ON_CONTEXT, c);
		}
		return null;
	}

	@Override
	@Nullable
	public Consumer<? super IN> onNextCall() {
		if ((options & ON_NEXT) == ON_NEXT && (level != Level.INFO || log.isInfoEnabled())) {
			return d -> safeLog(SignalType.ON_NEXT, d);
		}
		return null;
	}

	@Override
	@Nullable
	public Consumer<? super Throwable> onErrorCall() {
		boolean shouldLogAsDebug = level == Level.FINE && log.isDebugEnabled();
		boolean shouldLogAsTrace = level == Level.FINEST && log.isTraceEnabled();
		boolean shouldLogAsError = level != Level.FINE && level != Level.FINEST && log.isErrorEnabled();
		if ((options & ON_ERROR) == ON_ERROR && (shouldLogAsError || shouldLogAsDebug ||
				shouldLogAsTrace)) {
			String line = fuseable ? LOG_TEMPLATE_FUSEABLE : LOG_TEMPLATE;
			if (operatorLine != null) {
				line = line + " " + operatorLine;
			}
			String s = line;
			if (shouldLogAsTrace) {
				return e -> {
					log.trace(s, SignalType.ON_ERROR, e, source);
					log.trace("", e);
				};
			}
			else if (shouldLogAsDebug) {
				return e -> {
					log.debug(s, SignalType.ON_ERROR, e, source);
					log.debug("", e);
				};
			}
			else { //shouldLogAsError
				return e -> {
					log.error(s, SignalType.ON_ERROR, e, source);
					log.error("", e);
				};
			}
		}
		return null;
	}

	@Override
	@Nullable
	public Runnable onCompleteCall() {
		if ((options & ON_COMPLETE) == ON_COMPLETE && (level != Level.INFO || log.isInfoEnabled())) {
			return () -> log(SignalType.ON_COMPLETE, "");
		}
		return null;
	}

	@Override
	@Nullable
	public Runnable onAfterTerminateCall() {
		if ((options & AFTER_TERMINATE) == AFTER_TERMINATE && (level != Level.INFO || log.isInfoEnabled())) {
			return () -> log(SignalType.AFTER_TERMINATE, "");
		}
		return null;
	}

	@Override
	@Nullable
	public LongConsumer onRequestCall() {
		if ((options & REQUEST) == REQUEST && (level != Level.INFO || log.isInfoEnabled())) {
			return n -> log(SignalType.REQUEST,
					Long.MAX_VALUE == n ? "unbounded" : n);
		}
		return null;
	}

	@Override
	@Nullable
	public Runnable onCancelCall() {
		if ((options & CANCEL) == CANCEL && (level != Level.INFO || log.isInfoEnabled())) {
			return () -> log(SignalType.CANCEL, "");
		}
		return null;
	}

	@Override
	public String toString() {
		return "/loggers/" + log.getName() + "/" + id;
	}

}