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

org.zodiac.reactor.util.FastThreadLocalLocaleUtil Maven / Gradle / Ivy

The newest version!
package org.zodiac.reactor.util;

import java.util.Locale;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.annotation.Nonnull;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import org.zodiac.sdk.toolkit.util.LocaleUtil;

import io.netty.util.concurrent.FastThreadLocal;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.BaseSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Signal;
import reactor.core.publisher.SignalType;
import reactor.util.context.Context;

public abstract class FastThreadLocalLocaleUtil extends LocaleUtil {

    protected FastThreadLocalLocaleUtil() {
    }

    public static final Locale DEFAULT_LOCALE = Locale.getDefault();

    private static final FastThreadLocal CONTEXT_THREAD_LOCAL = new FastThreadLocal<>();

    /**
     * Gets the current locale, and returns to the system default language if not set.
     *
     * @return The {@link Locale}.
     */
    public static Locale current() {
        Locale locale = CONTEXT_THREAD_LOCAL.get();
        if (locale == null) {
            locale = DEFAULT_LOCALE;
        }
        return locale;
    }

    /**
     * Executes functions in a specified region and can only be used in non-reactive synchronous operations. 
* For example: an internationalized message that transforms some properties in an entity class. *

In the logic of the function, the current language can be obtained by {@link #current()}.

* * @param The parameter type. * @param The type of value returned by the function. * @param data The parameter. * @param locale The {@link Locale}. * @param mapper The function. * @return The value returned by the function. */ public static R doWith(T data, Locale locale, BiFunction mapper) { Locale old = CONTEXT_THREAD_LOCAL.get(); try { CONTEXT_THREAD_LOCAL.set(locale); return mapper.apply(data, locale); } finally { CONTEXT_THREAD_LOCAL.set(old); } } /** * Use the specified area to perform certain operations. * * @param locale The {@link Locale}. * @param consumer The operation {@link Consumer}. */ public static void doWith(Locale locale, Consumer consumer) { Locale old = CONTEXT_THREAD_LOCAL.get(); try { CONTEXT_THREAD_LOCAL.set(locale); consumer.accept(locale); } finally { CONTEXT_THREAD_LOCAL.set(old); } } /** * Use the specified region as the locale, and use {@link #currentReactive()} downstream to get it.
*
monoOrFlux.contextWrite(FastThreadLocalLocalUtil.useLocale(locale))
* * @param locale The {@link Locale}. * @return Context constructor. */ public static Function useLocale(Locale locale) { return ctx -> ctx.put(Locale.class, locale); } /** * Get the current region in a reactive way. * * @return The {@link Locale}. */ public static Mono currentReactive() { return Mono.deferWithContext(ctx -> Mono.just(ctx.getOrDefault(Locale.class, DEFAULT_LOCALE))); } public static Mono doInReactive(Callable call) { return currentReactive() .handle((locale, sink) -> { Locale old = CONTEXT_THREAD_LOCAL.get(); try { CONTEXT_THREAD_LOCAL.set(locale); T data = call.call(); if (data != null) { sink.next(data); } } catch (Throwable e) { sink.error(e); } finally { CONTEXT_THREAD_LOCAL.set(old); } }); } /** * Get the area in the reactive and perform the specified operation. * * @param The item type. * @param type The {@link SignalType}. * @param operation The operation. * @return The operation {@link Consumer}. */ public static Consumer> on(SignalType type, BiConsumer, Locale> operation) { return signal -> { if (signal.getType() != type) { return; } Locale locale = signal.getContext().getOrDefault(Locale.class, DEFAULT_LOCALE); doWith(locale, l -> operation.accept(signal, l)); }; } /** * Get the region in each cycle of the reactive and perform the specified operation. * *
     *     monoOrFlux
     *     .as(LocaleUtils.doOn(ON_NEXT,(signal,locale)-> ... ))
     *     ...
     * 
* * @param The item type. * @param The reactive stream type. * @param type The {@link SignalType}. * @param operation The operation. * @return Original stream. */ @SuppressWarnings("all") public static > Function doOn(SignalType type, BiConsumer, Locale> operation) { return publisher -> { if (publisher instanceof Mono) { return (T) Mono .from(publisher) .doOnEach(on(type, operation)); } return (T) Flux .from(publisher) .doOnEach(on(type, operation)); }; } /** *
     * monoOrFlux
     * .as(LocaleUtils.doOnNext(element-> .... ))
     * ...
     * 
* * @param The item type. * @param The reactive stream type. * @param operation The operation. * @return Original stream. */ public static > Function doOnNext(Consumer operation) { return doOn(SignalType.ON_NEXT, (s, l) -> operation.accept(s.get())); } /** *
     * monoOrFlux
     * .as(LocaleUtils.doOnNext((element,locale)-> .... ))
     * ...
     * 
* * @param The item type. * @param The reactive stream type. * @param operation The operation. * @return Original stream. */ public static > Function doOnNext(BiConsumer operation) { return doOn(SignalType.ON_NEXT, (s, l) -> operation.accept(s.get(), l)); } /** *
     * monoOrFlux
     * .as(LocaleUtils.doOnError(error-> .... ))
     * ...
     * 
* * @param The item type. * @param The reactive stream type. * @param operation The operation. * @return Original stream. */ public static > Function doOnError(Consumer operation) { return doOn(SignalType.ON_ERROR, (s, l) -> operation.accept(s.getThrowable())); } /** *
     * monoOrFlux
     * .as(LocaleUtils.doOnError((error,locale)-> .... ))
     * ...
     * 
* * @param The item type. * @param The reactive stream type. * @param operation The operation. * @return Original stream. */ public static > Function doOnError(BiConsumer operation) { return doOn(SignalType.ON_ERROR, (s, l) -> operation.accept(s.getThrowable(), l)); } public static Flux transform(Flux flux) { return new LocaleFlux<>(flux); } public static Mono transform(Mono mono) { return new LocaleMono<>(mono); } static class LocaleMono extends Mono { private final Mono source; public LocaleMono(Mono source) { super(); this.source = source; } @Override public void subscribe(@Nonnull CoreSubscriber actual) { doWith(actual, actual.currentContext().getOrDefault(Locale.class, DEFAULT_LOCALE), (a, l) -> { source.subscribe( new LocaleSwitchSubscriber<>(a) ); return null; } ); } } static class LocaleFlux extends Flux { private final Flux source; public LocaleFlux(Flux source) { super(); this.source = source; } @Override public void subscribe(@Nonnull CoreSubscriber actual) { doWith(actual, actual.currentContext().getOrDefault(Locale.class, DEFAULT_LOCALE), (a, l) -> { source.subscribe( new LocaleSwitchSubscriber<>(a) ); return null; } ); } } static class LocaleSwitchSubscriber extends BaseSubscriber { private final CoreSubscriber actual; public LocaleSwitchSubscriber(CoreSubscriber actual) { super(); this.actual = actual; } @Override @Nonnull public Context currentContext() { return actual .currentContext(); } @Override protected void hookOnSubscribe(@Nonnull Subscription subscription) { actual.onSubscribe(this); } private Locale current() { return currentContext() .getOrDefault(Locale.class, DEFAULT_LOCALE); } @Override protected void hookOnComplete() { doWith(current(), (l) -> actual.onComplete()); } @Override protected void hookOnError(@Nonnull Throwable error) { doWith(error, current(), (v, l) -> { actual.onError(v); return null; }); } @Override protected void hookOnNext(@Nonnull T value) { doWith(value, current(), (v, l) -> { actual.onNext(v); return null; }); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy