Schedule FreeMarker template lookup on bounded elastic scheduler
This commit makes sure that FreeMarker template lookups, which potentially block, are scheduled on the bounded elastic scheduler. Closes gh-30903
This commit is contained in:
parent
d41f546c43
commit
34477b4f03
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -18,6 +18,8 @@ package org.springframework.web.reactive.result.view;
|
|||
|
||||
import java.util.Locale;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
|
@ -81,6 +83,18 @@ public abstract class AbstractUrlBasedView extends AbstractView implements Initi
|
|||
*/
|
||||
public abstract boolean checkResourceExists(Locale locale) throws Exception;
|
||||
|
||||
/**
|
||||
* Deferred check whether the resource for the configured URL actually exists.
|
||||
* <p>The default implementation calls {@link #checkResourceExists(Locale)}.
|
||||
* @param locale the desired Locale that we're looking for
|
||||
* @return {@code false} if the resource exists
|
||||
* {@code false} if we know that it does not exist
|
||||
* @since 6.1
|
||||
*/
|
||||
public Mono<Boolean> resourceExists(Locale locale) {
|
||||
return Mono.fromCallable(() -> checkResourceExists(locale));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -238,12 +238,8 @@ public class UrlBasedViewResolver extends ViewResolverSupport
|
|||
}
|
||||
|
||||
View view = applyLifecycleMethods(viewName, urlBasedView);
|
||||
try {
|
||||
return (urlBasedView.checkResourceExists(locale) ? Mono.just(view) : Mono.empty());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
return urlBasedView.resourceExists(locale)
|
||||
.flatMap(exists -> exists ? Mono.just(view) : Mono.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -32,8 +32,10 @@ import freemarker.template.DefaultObjectWrapperBuilder;
|
|||
import freemarker.template.ObjectWrapper;
|
||||
import freemarker.template.SimpleHash;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.Version;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
|
|
@ -210,6 +212,24 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the FreeMarker template used for this view exists and is valid.
|
||||
* <p>Can be overridden to customize the behavior, for example in case of
|
||||
* multiple templates to be rendered into a single view.
|
||||
* @since 6.1
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> resourceExists(Locale locale) {
|
||||
return lookupTemplate(locale)
|
||||
.map(template -> Boolean.TRUE)
|
||||
.switchIfEmpty(Mono.just(Boolean.FALSE))
|
||||
.onErrorResume(FileNotFoundException.class, t -> Mono.just(Boolean.FALSE))
|
||||
.onErrorMap(ParseException.class, ex -> new ApplicationContextException(
|
||||
"Failed to parse FreeMarker template for URL [" + getUrl() + "]", ex))
|
||||
.onErrorMap(IOException.class, ex -> new ApplicationContextException(
|
||||
"Could not load FreeMarker template for URL [" + getUrl() + "]", ex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the model to use for rendering by potentially exposing a
|
||||
* {@link RequestContext} for use in Spring FreeMarker macros and then
|
||||
|
|
@ -243,7 +263,7 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
|||
@Nullable MediaType contentType, ServerWebExchange exchange) {
|
||||
|
||||
return exchange.getResponse().writeWith(Mono
|
||||
.fromCallable(() -> {
|
||||
.defer(() -> {
|
||||
// Expose all standard FreeMarker hash models.
|
||||
SimpleHash freeMarkerModel = getTemplateModel(renderAttributes, exchange);
|
||||
|
||||
|
|
@ -252,19 +272,22 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
|||
}
|
||||
|
||||
Locale locale = LocaleContextHolder.getLocale(exchange.getLocaleContext());
|
||||
FastByteArrayOutputStream bos = new FastByteArrayOutputStream();
|
||||
try {
|
||||
Charset charset = getCharset(contentType);
|
||||
Writer writer = new OutputStreamWriter(bos, charset);
|
||||
getTemplate(locale).process(freeMarkerModel, writer);
|
||||
|
||||
byte[] bytes = bos.toByteArrayUnsafe();
|
||||
return exchange.getResponse().bufferFactory().wrap(bytes);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
String message = "Could not load FreeMarker template for URL [" + getUrl() + "]";
|
||||
throw new IllegalStateException(message, ex);
|
||||
}
|
||||
return lookupTemplate(locale)
|
||||
.flatMap(template -> {
|
||||
try {
|
||||
FastByteArrayOutputStream bos = new FastByteArrayOutputStream();
|
||||
Charset charset = getCharset(contentType);
|
||||
Writer writer = new OutputStreamWriter(bos, charset);
|
||||
template.process(freeMarkerModel, writer);
|
||||
byte[] bytes = bos.toByteArrayUnsafe();
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
|
||||
return Mono.just(buffer);
|
||||
}
|
||||
catch (TemplateException | IOException ex ) {
|
||||
String message = "Could not load FreeMarker template for URL [" + getUrl() + "]";
|
||||
return Mono.error(new IllegalStateException(message, ex));
|
||||
}
|
||||
});
|
||||
})
|
||||
.doOnDiscard(DataBuffer.class, DataBufferUtils::release));
|
||||
}
|
||||
|
|
@ -302,11 +325,32 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
|||
* <p>By default, the template specified by the "url" bean property will be retrieved.
|
||||
* @param locale the current locale
|
||||
* @return the FreeMarker template to render
|
||||
* @deprecated since 6.1, in favor of {@link #lookupTemplate(Locale)}, to be
|
||||
* removed in 6.2
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
protected Template getTemplate(Locale locale) throws IOException {
|
||||
return (getEncoding() != null ?
|
||||
obtainConfiguration().getTemplate(getUrl(), locale, getEncoding()) :
|
||||
obtainConfiguration().getTemplate(getUrl(), locale));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the FreeMarker template for the given locale, to be rendered by this view.
|
||||
* <p>By default, the template specified by the "url" bean property will be retrieved,
|
||||
* and the returned mono will subscribe on the
|
||||
* {@linkplain Schedulers#boundedElastic() bounded elastic scheduler} as template
|
||||
* lookups can be blocking operations.
|
||||
* @param locale the current locale
|
||||
* @return the FreeMarker template to render
|
||||
* @since 6.1
|
||||
*/
|
||||
protected Mono<Template> lookupTemplate(Locale locale) {
|
||||
return Mono.fromCallable(() ->
|
||||
getEncoding() != null ?
|
||||
obtainConfiguration().getTemplate(getUrl(), locale, getEncoding()) :
|
||||
obtainConfiguration().getTemplate(getUrl(), locale))
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.condition.DisabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
|
@ -107,80 +108,84 @@ public class FreeMarkerMacroTests {
|
|||
view.setUrl("tmp.ftl");
|
||||
view.setConfiguration(this.freeMarkerConfig);
|
||||
|
||||
view.render(singletonMap("testBean", new TestBean("Dilbert", 99)), null, this.exchange).subscribe();
|
||||
Map<String, ?> model = singletonMap("testBean", new TestBean("Dilbert", 99));
|
||||
|
||||
assertThat(getOutput()).containsExactly("Hi Dilbert");
|
||||
StepVerifier.create(view.render(model, null, this.exchange)
|
||||
.then(Mono.fromCallable(this::getOutput)))
|
||||
.assertNext(l -> assertThat(l).containsExactly("Hi Dilbert"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void name() throws Exception {
|
||||
assertThat(getMacroOutput("NAME")).containsExactly("Darren");
|
||||
testMacroOutput("NAME", "Darren");
|
||||
}
|
||||
|
||||
private void testMacroOutput(String name, String... contents) throws Exception {
|
||||
StepVerifier.create(getMacroOutput(name))
|
||||
.assertNext(list -> assertThat(list).containsExactly(contents))
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledForJreRange(min = JRE.JAVA_21)
|
||||
public void age() throws Exception {
|
||||
assertThat(getMacroOutput("AGE")).containsExactly("99");
|
||||
testMacroOutput("AGE", "99");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void message() throws Exception {
|
||||
assertThat(getMacroOutput("MESSAGE")).containsExactly("Howdy Mundo");
|
||||
testMacroOutput("MESSAGE", "Howdy Mundo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessage() throws Exception {
|
||||
assertThat(getMacroOutput("DEFAULTMESSAGE")).containsExactly("hi planet");
|
||||
testMacroOutput("DEFAULTMESSAGE", "hi planet");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageArgs() throws Exception {
|
||||
assertThat(getMacroOutput("MESSAGEARGS")).containsExactly("Howdy[World]");
|
||||
testMacroOutput("MESSAGEARGS", "Howdy[World]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageArgsWithDefaultMessage() throws Exception {
|
||||
assertThat(getMacroOutput("MESSAGEARGSWITHDEFAULTMESSAGE")).containsExactly("Hi");
|
||||
testMacroOutput("MESSAGEARGSWITHDEFAULTMESSAGE", "Hi");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void url() throws Exception {
|
||||
assertThat(getMacroOutput("URL")).containsExactly("/springtest/aftercontext.html");
|
||||
testMacroOutput("URL", "/springtest/aftercontext.html");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urlParams() throws Exception {
|
||||
assertThat(getMacroOutput("URLPARAMS")).containsExactly(
|
||||
"/springtest/aftercontext/bar?spam=bucket");
|
||||
testMacroOutput("URLPARAMS", "/springtest/aftercontext/bar?spam=bucket");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formInput() throws Exception {
|
||||
assertThat(getMacroOutput("FORM1")).containsExactly(
|
||||
"<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" >");
|
||||
testMacroOutput("FORM1", "<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formInputWithCss() throws Exception {
|
||||
assertThat(getMacroOutput("FORM2")).containsExactly(
|
||||
"<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" class=\"myCssClass\" >");
|
||||
testMacroOutput("FORM2", "<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" class=\"myCssClass\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formTextarea() throws Exception {
|
||||
assertThat(getMacroOutput("FORM3")).containsExactly(
|
||||
"<textarea id=\"name\" name=\"name\" >Darren</textarea>");
|
||||
testMacroOutput("FORM3", "<textarea id=\"name\" name=\"name\" >Darren</textarea>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formTextareaWithCustomRowsAndColumns() throws Exception {
|
||||
assertThat(getMacroOutput("FORM4")).containsExactly(
|
||||
"<textarea id=\"name\" name=\"name\" rows=10 cols=30>Darren</textarea>");
|
||||
testMacroOutput("FORM4", "<textarea id=\"name\" name=\"name\" rows=10 cols=30>Darren</textarea>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formSingleSelectFromMap() throws Exception {
|
||||
assertThat(getMacroOutput("FORM5")).containsExactly(
|
||||
testMacroOutput("FORM5",
|
||||
"<select id=\"name\" name=\"name\" >", //
|
||||
"<option value=\"Rob&Harrop\">Rob Harrop</option>", //
|
||||
"<option value=\"John\">John Doe</option>", //
|
||||
|
|
@ -191,7 +196,7 @@ public class FreeMarkerMacroTests {
|
|||
|
||||
@Test
|
||||
public void formSingleSelectFromList() throws Exception {
|
||||
assertThat(getMacroOutput("FORM14")).containsExactly(
|
||||
testMacroOutput("FORM14",
|
||||
"<select id=\"name\" name=\"name\" >", //
|
||||
"<option value=\"Rob Harrop\">Rob Harrop</option>", //
|
||||
"<option value=\"Darren Davison\">Darren Davison</option>", //
|
||||
|
|
@ -202,7 +207,7 @@ public class FreeMarkerMacroTests {
|
|||
|
||||
@Test
|
||||
public void formMultiSelect() throws Exception {
|
||||
assertThat(getMacroOutput("FORM6")).containsExactly(
|
||||
testMacroOutput("FORM6",
|
||||
"<select multiple=\"multiple\" id=\"spouses\" name=\"spouses\" >", //
|
||||
"<option value=\"Rob&Harrop\">Rob Harrop</option>", //
|
||||
"<option value=\"John\">John Doe</option>", //
|
||||
|
|
@ -213,7 +218,7 @@ public class FreeMarkerMacroTests {
|
|||
|
||||
@Test
|
||||
public void formRadioButtons() throws Exception {
|
||||
assertThat(getMacroOutput("FORM7")).containsExactly(
|
||||
testMacroOutput("FORM7",
|
||||
"<input type=\"radio\" id=\"name0\" name=\"name\" value=\"Rob&Harrop\" >", //
|
||||
"<label for=\"name0\">Rob Harrop</label>", //
|
||||
"<input type=\"radio\" id=\"name1\" name=\"name\" value=\"John\" >", //
|
||||
|
|
@ -226,28 +231,28 @@ public class FreeMarkerMacroTests {
|
|||
|
||||
@Test
|
||||
public void formCheckboxForStringProperty() throws Exception {
|
||||
assertThat(getMacroOutput("FORM15")).containsExactly(
|
||||
testMacroOutput("FORM15",
|
||||
"<input type=\"hidden\" name=\"_name\" value=\"on\"/>",
|
||||
"<input type=\"checkbox\" id=\"name\" name=\"name\" />");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formCheckboxForBooleanProperty() throws Exception {
|
||||
assertThat(getMacroOutput("FORM16")).containsExactly(
|
||||
testMacroOutput("FORM16",
|
||||
"<input type=\"hidden\" name=\"_jedi\" value=\"on\"/>",
|
||||
"<input type=\"checkbox\" id=\"jedi\" name=\"jedi\" checked=\"checked\" />");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formCheckboxForNestedPath() throws Exception {
|
||||
assertThat(getMacroOutput("FORM18")).containsExactly(
|
||||
testMacroOutput("FORM18",
|
||||
"<input type=\"hidden\" name=\"_spouses[0].jedi\" value=\"on\"/>",
|
||||
"<input type=\"checkbox\" id=\"spouses0.jedi\" name=\"spouses[0].jedi\" checked=\"checked\" />");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formCheckboxForStringArray() throws Exception {
|
||||
assertThat(getMacroOutput("FORM8")).containsExactly(
|
||||
testMacroOutput("FORM8",
|
||||
"<input type=\"checkbox\" id=\"stringArray0\" name=\"stringArray\" value=\"Rob&Harrop\" >", //
|
||||
"<label for=\"stringArray0\">Rob Harrop</label>", //
|
||||
"<input type=\"checkbox\" id=\"stringArray1\" name=\"stringArray\" value=\"John\" checked=\"checked\" >", //
|
||||
|
|
@ -261,41 +266,41 @@ public class FreeMarkerMacroTests {
|
|||
|
||||
@Test
|
||||
public void formPasswordInput() throws Exception {
|
||||
assertThat(getMacroOutput("FORM9")).containsExactly(
|
||||
testMacroOutput("FORM9",
|
||||
"<input type=\"password\" id=\"name\" name=\"name\" value=\"\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formHiddenInput() throws Exception {
|
||||
assertThat(getMacroOutput("FORM10")).containsExactly(
|
||||
testMacroOutput("FORM10",
|
||||
"<input type=\"hidden\" id=\"name\" name=\"name\" value=\"Darren\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formInputText() throws Exception {
|
||||
assertThat(getMacroOutput("FORM11")).containsExactly(
|
||||
testMacroOutput("FORM11",
|
||||
"<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formInputHidden() throws Exception {
|
||||
assertThat(getMacroOutput("FORM12")).containsExactly(
|
||||
testMacroOutput("FORM12",
|
||||
"<input type=\"hidden\" id=\"name\" name=\"name\" value=\"Darren\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formInputPassword() throws Exception {
|
||||
assertThat(getMacroOutput("FORM13")).containsExactly(
|
||||
testMacroOutput("FORM13",
|
||||
"<input type=\"password\" id=\"name\" name=\"name\" value=\"\" >");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forInputWithNestedPath() throws Exception {
|
||||
assertThat(getMacroOutput("FORM17")).containsExactly(
|
||||
testMacroOutput("FORM17",
|
||||
"<input type=\"text\" id=\"spouses0.name\" name=\"spouses[0].name\" value=\"Fred\" >");
|
||||
}
|
||||
|
||||
private List<String> getMacroOutput(String name) throws Exception {
|
||||
private Mono<List<String>> getMacroOutput(String name) throws Exception {
|
||||
String macro = fetchMacro(name);
|
||||
assertThat(macro).isNotNull();
|
||||
storeTemplateInTempDir(macro);
|
||||
|
|
@ -336,9 +341,8 @@ public class FreeMarkerMacroTests {
|
|||
view.setExposeSpringMacroHelpers(false);
|
||||
view.setConfiguration(freeMarkerConfig);
|
||||
|
||||
view.render(model, null, this.exchange).subscribe();
|
||||
|
||||
return getOutput();
|
||||
return view.render(model, null, this.exchange).
|
||||
then(Mono.fromCallable(this::getOutput));
|
||||
}
|
||||
|
||||
private static String fetchMacro(String name) throws Exception {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.web.reactive.result.view.freemarker;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
|
|
@ -28,7 +27,6 @@ import reactor.test.StepVerifier;
|
|||
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
|
@ -101,6 +99,26 @@ class FreeMarkerViewTests {
|
|||
assertThat(freeMarkerView.checkResourceExists(Locale.US)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceExists() {
|
||||
freeMarkerView.setConfiguration(this.freeMarkerConfig);
|
||||
freeMarkerView.setUrl("test.ftl");
|
||||
|
||||
StepVerifier.create(freeMarkerView.resourceExists(Locale.US))
|
||||
.assertNext(b -> assertThat(b).isTrue())
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceDoesNotExists() {
|
||||
freeMarkerView.setConfiguration(this.freeMarkerConfig);
|
||||
freeMarkerView.setUrl("foo-bar.ftl");
|
||||
|
||||
StepVerifier.create(freeMarkerView.resourceExists(Locale.US))
|
||||
.assertNext(b -> assertThat(b).isFalse())
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void render() {
|
||||
freeMarkerView.setApplicationContext(this.context);
|
||||
|
|
@ -112,7 +130,8 @@ class FreeMarkerViewTests {
|
|||
freeMarkerView.render(model, null, this.exchange).block(Duration.ofMillis(5000));
|
||||
|
||||
StepVerifier.create(this.exchange.getResponse().getBody())
|
||||
.consumeNextWith(buf -> assertThat(asString(buf)).isEqualTo("<html><body>hi FreeMarker</body></html>"))
|
||||
.consumeNextWith(buf -> assertThat(buf.toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("<html><body>hi FreeMarker</body></html>"))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
|
@ -138,15 +157,6 @@ class FreeMarkerViewTests {
|
|||
}
|
||||
|
||||
|
||||
private static String asString(DataBuffer dataBuffer) {
|
||||
@SuppressWarnings("deprecation")
|
||||
ByteBuffer byteBuffer = dataBuffer.toByteBuffer();
|
||||
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||
byteBuffer.get(bytes);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String handle() {
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue