/** * Propagate the signal represented by this {@link Signal} instance to a * given {@link Subscriber}. * * @param observer the {@link Subscriber} to play the {@link Signal} on */ @Override default void accept(Subscriber<? super T> observer) { if (isOnNext()) { observer.onNext(get()); } else if (isOnComplete()) { observer.onComplete(); } else if (isOnError()) { observer.onError(getThrowable()); } else if (isOnSubscribe()) { observer.onSubscribe(getSubscription()); } } }
@Test public void equalsIgnoresContext() { Signal<String> next1 = Signal.next("foo"); Signal<String> next2 = Signal.next("foo", Context.of("bar", "baz")); assertThat(next1.getContext().isEmpty()).as("next1 context empty").isTrue(); assertThat(next2.getContext().isEmpty()).as("next2 context not empty").isFalse(); assertThat(next1).isEqualTo(next2); } }
@Override public void onComplete() { signalCached(Signal.complete()); }
/** * Read whether this signal is on error and carries the cause. * * @return a boolean indicating whether this signal has an error */ default boolean hasError() { return isOnError() && getThrowable() != null; }
MonoCacheTime(Mono<? extends T> source, Function<? super T, Duration> valueTtlGenerator, Function<Throwable, Duration> errorTtlGenerator, Supplier<Duration> emptyTtlGenerator, Scheduler clock) { super(source); this.ttlGenerator = sig -> { if (sig.isOnNext()) return valueTtlGenerator.apply(sig.get()); if (sig.isOnError()) return errorTtlGenerator.apply(sig.getThrowable()); return emptyTtlGenerator.get(); }; this.clock = clock; @SuppressWarnings("unchecked") Signal<T> emptyState = (Signal<T>) EMPTY; this.state = emptyState; }
@Test public void completeState(){ Signal<Integer> s = Signal.complete(); assertThat(s.isOnComplete()).isTrue(); assertThat(s.isOnSubscribe()).isFalse(); assertThat(s.hasError()).isFalse(); assertThat(s.hasValue()).isFalse(); assertThat(s).isEqualTo(Signal.complete()); assertThat(s).isNotEqualTo(Signal.error(e)); assertThat(s).isNotEqualTo(Signal.subscribe(Operators.emptySubscription())); assertThat(s).isNotEqualTo(Signal.next(1)); assertThat(s.hashCode()).isEqualTo(Signal.complete().hashCode()); assertThat(s.hashCode()).isNotEqualTo(Signal.error(e).hashCode()); assertThat(s.hashCode()).isNotEqualTo(Signal.next(1).hashCode()); assertThat(s.hashCode()).isNotEqualTo(Signal.subscribe(Operators.emptySubscription()).hashCode()); assertThat(Signal.isComplete(s)).isTrue(); assertThat(Signal.isError(s)).isFalse(); assertThat(s.getType()).isEqualTo(SignalType.ON_COMPLETE); assertThat(s.toString()).contains("onComplete"); StepVerifier.create(Flux.<Integer>from(sub -> { sub.onSubscribe(Operators.emptySubscription()); s.accept(sub); })) .verifyComplete(); }
@Test public void testDoOnEachSignal() { List<Signal<Integer>> signals = new ArrayList<>(4); Mono<Integer> mono = Mono.just(1) .doOnEach(signals::add); StepVerifier.create(mono) .expectSubscription() .expectNext(1) .expectComplete() .verify(); assertThat(signals.size(), is(2)); assertThat("onNext", signals.get(0).get(), is(1)); assertTrue("onComplete expected", signals.get(1).isOnComplete()); }
@Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (o == null || !(o instanceof Signal)) { return false; } Signal<?> signal = (Signal<?>) o; if (getType() != signal.getType()) { return false; } if (isOnComplete()) { return true; } if (isOnSubscribe()) { return Objects.equals(this.getSubscription(), signal.getSubscription()); } if (isOnError()) { return Objects.equals(this.getThrowable(), signal.getThrowable()); } if (isOnNext()) { return Objects.equals(this.get(), signal.get()); } return false; }
@Override public Mono<ClientResponse> filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) { return exchangeFunction.exchange(clientRequest).doOnEach((signal) -> { if (!signal.isOnComplete()) { Long startTime = signal.getContext().get(METRICS_WEBCLIENT_START_TIME); ClientResponse clientResponse = signal.get(); Throwable throwable = signal.getThrowable(); Iterable<Tag> tags = this.tagProvider.tags(clientRequest, clientResponse, throwable); Timer.builder(this.metricName).tags(tags) .description("Timer of WebClient operation") .register(this.meterRegistry) .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); } }).subscriberContext((context) -> context.put(METRICS_WEBCLIENT_START_TIME, System.nanoTime())); }
boolean onSignal(Signal<T> actualSignal) { SignalEvent<T> signalEvent = (SignalEvent<T>) this.script.poll(); Optional<AssertionError> error = signalEvent.test(actualSignal); if (error.isPresent()) { Exceptions.addThrowable(ERRORS, this, error.get()); // #55 ensure the onError is added as a suppressed to the AssertionError if(actualSignal.isOnError()) { error.get().addSuppressed(actualSignal.getThrowable()); } maybeCancel(actualSignal); this.completeLatch.countDown(); return true; } if (actualSignal.isOnNext()) { unasserted--; } return false; }
Flux.just("A", "B") .doOnEach(sig -> { if (sig.isOnComplete()) { Context ctx = sig.getContext(); contexts.add(ctx); repeats.add("emitted " + ctx.get("emitted") + " elements this attempt, " + ctx.get("repeatsLeft") + " repeats left");
@Override public DefaultStepVerifierBuilder<T> expectSubscriptionMatches( Predicate<? super Subscription> predicate) { Objects.requireNonNull(predicate, "predicate"); this.script.set(0, new SignalEvent<>((signal, se) -> { if (!signal.isOnSubscribe()) { return errorFormatter.failOptional(se, "expected: onSubscribe(); actual: %s", signal); } else if (!predicate.test(signal.getSubscription())) { return errorFormatter.failOptional(se, "predicate failed on subscription: %s", signal.getSubscription()); } else { return Optional.empty(); } }, "expectSubscriptionMatches")); return this; }
@Override public void onError(Throwable t) { this.terminalError = Signal.error(t); onExpectation(terminalError); this.completeLatch.countDown(); }
Flux.error(new IllegalStateException("boom")) .doOnEach(sig -> { retriesLeft.add(sig.getContext().get("retriesLeft")); if (!sig.isOnNext()) { contextPerRetry.add(sig.getContext());
/** Cancels this subscriber if the actual signal is null or not a complete/error */ final void maybeCancel(@Nullable Signal<T> actualSignal) { if (actualSignal == null || (!actualSignal.isOnComplete() && !actualSignal.isOnError())) { cancel(); } }
@Override public DefaultStepVerifier<T> expectComplete() { SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnComplete()) { return errorFormatter.failOptional(se, "expected: onComplete(); actual: %s", signal); } else { return Optional.empty(); } }, "expectComplete"); this.script.add(event); return build(); }
/** * Creates and returns a {@code Signal} of variety {@code Type.NEXT}, which holds * the value. * <p> * Note that this variant associates an empty {@link Context} with the {@link Signal}. * * @param <T> the value type * @param t the value item associated to the signal * * @return an {@code OnNext} variety of {@code Signal} */ static <T> Signal<T> next(T t) { return next(t, Context.empty()); }
@Override @Nullable public Object scanUnsafe(Attr key) { if (key == Attr.PARENT) return s; if (key == Attr.TERMINATED) return terminalSignal != null; if (key == Attr.ERROR) return terminalSignal != null ? terminalSignal.getThrowable() : null; if (key == Attr.CANCELLED) return getAsBoolean(); if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested; if (key == Attr.BUFFERED) return size(); return InnerOperator.super.scanUnsafe(key); }