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 super T> 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 super T> 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