working on response flush bugs, move logging to FINEST level

This commit is contained in:
Jörg Prante 2023-03-31 17:54:30 +02:00
parent 2778869d14
commit 93ac72ea0c
20 changed files with 195 additions and 132 deletions

View file

@ -137,13 +137,17 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
internalFileWrite(fileChannel, bufferSize, true); internalFileWrite(fileChannel, bufferSize, true);
} else if (inputStream != null) { } else if (inputStream != null) {
internalStreamWrite(inputStream, bufferSize, true); internalStreamWrite(inputStream, bufferSize, true);
} else {
logger.log(Level.FINEST, "no content, we assume header only");
flush();
} }
return new HttpResponse(this); return new HttpResponse(this);
} }
public void flush() { public void flush() {
internalBufferWrite(Unpooled.buffer(0)); logger.log(Level.FINEST, "flush netty response");
this.dataBuffer = NettyDataBufferFactory.getInstance().wrap(Unpooled.buffer(0));
internalBufferWrite(dataBuffer);
} }
@Override @Override
@ -187,23 +191,23 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
} }
HttpHeaders trailingHeaders = new DefaultHttpHeaders(); HttpHeaders trailingHeaders = new DefaultHttpHeaders();
super.trailingHeaders.entries().forEach(e -> trailingHeaders.add(e.getKey(), e.getValue())); super.trailingHeaders.entries().forEach(e -> trailingHeaders.add(e.getKey(), e.getValue()));
ctx.channel().eventLoop().execute(() -> { HttpVersion httpVersion = HttpVersion.valueOf(version.text());
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.valueOf(version.text()), FullHttpResponse fullHttpResponse =
responseStatus, byteBuf.retain(), headers, trailingHeaders); new DefaultFullHttpResponse(httpVersion, responseStatus, byteBuf.retain(), headers, trailingHeaders);
ChannelFuture channelFuture; ChannelFuture channelFuture;
if (sequenceId != null) { if (sequenceId != null) {
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse, HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
ctx.channel().newPromise(), sequenceId); ctx.channel().newPromise(), sequenceId);
channelFuture = ctx.write(httpPipelinedResponse); channelFuture = ctx.write(httpPipelinedResponse);
} else { } else {
channelFuture = ctx.write(fullHttpResponse); channelFuture = ctx.write(fullHttpResponse);
} }
if (!keepAlive || shouldClose()) { if (!keepAlive || shouldClose()) {
logger.log(Level.FINER, "adding close listener to channel future " + channelFuture); logger.log(Level.FINEST, "adding close listener to channel future " + channelFuture);
channelFuture.addListener(CLOSE); channelFuture.addListener(CLOSE);
} }
ctx.flush(); logger.log(Level.FINEST, "flush netty ctx");
}); ctx.flush();
} }
private void internalFileWrite(FileChannel fileChannel, int bufferSize, boolean keepAlive) { private void internalFileWrite(FileChannel fileChannel, int bufferSize, boolean keepAlive) {
@ -213,20 +217,19 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
} }
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(status.code()); HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(status.code());
DefaultHttpResponse rsp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, responseStatus); DefaultHttpResponse rsp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, responseStatus);
ctx.channel().eventLoop().execute(() -> { ctx.write(rsp);
ctx.write(rsp); try {
try { ctx.write(new ChunkedNioFile(fileChannel, bufferSize));
ctx.write(new ChunkedNioFile(fileChannel, bufferSize)); } catch (IOException e) {
} catch (IOException e) { throw new UncheckedIOException(e);
throw new UncheckedIOException(e); }
} ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!keepAlive || shouldClose()) {
if (!keepAlive || shouldClose()) { logger.log(Level.FINEST, "adding close listener to channel future " + channelFuture);
logger.log(Level.FINER, "adding close listener to channel future " + channelFuture); channelFuture.addListener(CLOSE);
channelFuture.addListener(CLOSE); }
} logger.log(Level.FINEST, "flush netty ctx");
ctx.flush(); ctx.flush();
});
} }
private void internalStreamWrite(InputStream inputStream, int bufferSize, boolean keepAlive) { private void internalStreamWrite(InputStream inputStream, int bufferSize, boolean keepAlive) {
@ -260,25 +263,24 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
} }
HttpHeaders trailingHeaders = new DefaultHttpHeaders(); HttpHeaders trailingHeaders = new DefaultHttpHeaders();
super.trailingHeaders.entries().forEach(e -> trailingHeaders.add(e.getKey(), e.getValue())); super.trailingHeaders.entries().forEach(e -> trailingHeaders.add(e.getKey(), e.getValue()));
ctx.channel().eventLoop().execute(() -> { DefaultHttpResponse defaultHttpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, responseStatus);
DefaultHttpResponse defaultHttpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, responseStatus); if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH)) { headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); } else {
} else { if (keepAlive) {
if (keepAlive) { headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
} }
defaultHttpResponse.headers().set(headers); }
ctx.write(defaultHttpResponse); defaultHttpResponse.headers().set(headers);
ctx.write(new ChunkedStream(inputStream, bufferSize)); ctx.write(defaultHttpResponse);
ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); ctx.write(new ChunkedStream(inputStream, bufferSize));
if (!keepAlive || shouldClose) { ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
logger.log(Level.FINER, "adding close listener to channel future " + channelFuture); if (!keepAlive || shouldClose) {
channelFuture.addListener(CLOSE); logger.log(Level.FINEST, "adding close listener to channel future " + channelFuture);
} channelFuture.addListener(CLOSE);
ctx.flush(); }
}); logger.log(Level.FINEST, "flush netty ctx");
ctx.flush();
} }
} }
} }

View file

@ -64,7 +64,7 @@ public class NettyHttpServer implements HttpServer {
this.serviceLoader = ServiceLoader.load(HttpChannelInitializer.class); this.serviceLoader = ServiceLoader.load(HttpChannelInitializer.class);
this.channelFutures = new ArrayList<>(); this.channelFutures = new ArrayList<>();
this.channels = new ArrayList<>(); this.channels = new ArrayList<>();
logger.log(Level.INFO, "parent event loop group = " + parentEventLoopGroup + logger.log(Level.FINE, "parent event loop group = " + parentEventLoopGroup +
" child event loop group = " + childEventLoopGroup + " child event loop group = " + childEventLoopGroup +
" socket channel class = " + socketChannelClass + " socket channel class = " + socketChannelClass +
" router addresses = " + getApplication().getAddresses()); " router addresses = " + getApplication().getAddresses());
@ -85,7 +85,7 @@ public class NettyHttpServer implements HttpServer {
@Override @Override
public void bind() throws BindException { public void bind() throws BindException {
Set<HttpAddress> httpAddressSet = getApplication().getAddresses(); Set<HttpAddress> httpAddressSet = getApplication().getAddresses();
logger.log(Level.INFO, "http adresses = " + httpAddressSet); logger.log(Level.FINE, "http adresses = " + httpAddressSet);
for (HttpAddress httpAddress : httpAddressSet) { for (HttpAddress httpAddress : httpAddressSet) {
SocketConfig socketConfig = httpAddress.getSocketConfig(); SocketConfig socketConfig = httpAddress.getSocketConfig();
ServerBootstrap bootstrap = new ServerBootstrap() ServerBootstrap bootstrap = new ServerBootstrap()
@ -124,7 +124,7 @@ public class NettyHttpServer implements HttpServer {
throw new IOException("unable to bind to " + httpAddress + " because network class " + throw new IOException("unable to bind to " + httpAddress + " because network class " +
detectedNetworkClass + " is not allowed by configured network class " + configuredNetworkClass); detectedNetworkClass + " is not allowed by configured network class " + configuredNetworkClass);
} }
logger.log(Level.INFO, () -> "trying to bind to " + inetSocketAddress); logger.log(Level.FINE, () -> "trying to bind to " + inetSocketAddress);
channelFutures.add(bootstrap.bind(inetSocketAddress)); channelFutures.add(bootstrap.bind(inetSocketAddress));
} catch (IOException e) { } catch (IOException e) {
throw new BindException(e.getMessage()); throw new BindException(e.getMessage());
@ -139,7 +139,7 @@ public class NettyHttpServer implements HttpServer {
}); });
channels.add(channelFuture.sync().channel()); channels.add(channelFuture.sync().channel());
channelFuture.await(); channelFuture.await();
logger.log(Level.FINER, () -> channelFuture.channel() + " ready, listening"); logger.log(Level.FINE, () -> channelFuture.channel() + " ready, listening");
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.log(Level.WARNING, e.getMessage(), e); logger.log(Level.WARNING, e.getMessage(), e);
} }

View file

@ -340,7 +340,7 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public void done() { public void done() {
this.done = true; this.done = true;
logger.log(Level.FINER, "done"); logger.log(Level.FINEST, "done");
} }
@Override @Override
@ -379,7 +379,7 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
if (httpServerConfig != null && httpServerConfig.getServerName() != null) { if (httpServerConfig != null && httpServerConfig.getServerName() != null) {
headers.add(HttpHeaderNames.SERVER, httpServerConfig.getServerName()); headers.add(HttpHeaderNames.SERVER, httpServerConfig.getServerName());
} }
logger.log(Level.FINER, "build headers: status = " + status + " headers = " + headers); logger.log(Level.FINEST, () -> "headers: status = " + status + " headers = " + headers);
} }
public CharBuffer wrapHeaders() { public CharBuffer wrapHeaders() {

View file

@ -75,6 +75,11 @@ public class BaseHttpServerContext implements HttpServerContext {
this.attributes.put("ctx", this); this.attributes.put("ctx", this);
} }
@Override
public Application getApplication() {
return application;
}
@Override @Override
public HttpRequestBuilder request() { public HttpRequestBuilder request() {
return httpRequestBuilder; return httpRequestBuilder;

View file

@ -9,11 +9,14 @@ import java.nio.file.Path;
import org.xbib.net.Attributes; import org.xbib.net.Attributes;
import org.xbib.net.URL; import org.xbib.net.URL;
import org.xbib.net.buffer.DataBuffer; import org.xbib.net.buffer.DataBuffer;
import org.xbib.net.http.server.application.Application;
import org.xbib.net.http.server.route.HttpRouteResolver; import org.xbib.net.http.server.route.HttpRouteResolver;
import org.xbib.net.http.server.service.HttpService; import org.xbib.net.http.server.service.HttpService;
public interface HttpServerContext { public interface HttpServerContext {
Application getApplication();
HttpRequestBuilder request(); HttpRequestBuilder request();
HttpResponseBuilder response(); HttpResponseBuilder response();

View file

@ -14,6 +14,7 @@ import org.xbib.net.http.server.HttpResponseBuilder;
import org.xbib.net.http.server.HttpServerContext; import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.domain.HttpDomain; import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.session.SessionListener; import org.xbib.net.http.server.session.SessionListener;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
public interface Application extends SessionListener, Resolver<Path>, Closeable { public interface Application extends SessionListener, Resolver<Path>, Closeable {
@ -26,6 +27,8 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
ZoneId getZoneId(); ZoneId getZoneId();
MimeTypeService getMimeService();
Path getHome(); Path getHome();
String getContextPath(); String getContextPath();

View file

@ -5,6 +5,7 @@ import java.time.ZoneId;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService;
public interface ApplicationBuilder { public interface ApplicationBuilder {
@ -30,5 +31,11 @@ public interface ApplicationBuilder {
ApplicationBuilder setZoneId(ZoneId zoneId); ApplicationBuilder setZoneId(ZoneId zoneId);
ApplicationBuilder setMimeTypeService(MimeTypeService mimeTypeService);
ApplicationBuilder setStaticSuffixes(String... suffixes);
ApplicationBuilder registerModule(ApplicationModule applicationModule);
Application build(); Application build();
} }

View file

@ -19,7 +19,7 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
long keepAliveTime, TimeUnit timeUnit, long keepAliveTime, TimeUnit timeUnit,
ThreadFactory threadFactory) { ThreadFactory threadFactory) {
super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory); super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory);
logger.log(Level.FINE, "threadpool executor up with nThreads = " + nThreads + logger.log(Level.FINE, () -> "threadpool executor up with nThreads = " + nThreads +
" keepAliveTime = " + keepAliveTime + " keepAliveTime = " + keepAliveTime +
" time unit = " + timeUnit + " time unit = " + timeUnit +
" maxQueue = " + maxQueue + " maxQueue = " + maxQueue +
@ -38,14 +38,14 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
@Override @Override
protected void afterExecute(Runnable runnable, Throwable terminationCause) { protected void afterExecute(Runnable runnable, Throwable terminationCause) {
super.afterExecute(runnable, terminationCause); super.afterExecute(runnable, terminationCause);
logger.log(Level.FINEST, "after execute of " + runnable); logger.log(Level.FINEST, () -> "after execute of " + runnable);
if (terminationCause != null) { if (terminationCause != null) {
logger.log(Level.SEVERE, terminationCause.getMessage(), terminationCause); logger.log(Level.SEVERE, terminationCause.getMessage(), terminationCause);
return; return;
} }
if (runnable instanceof RouterTask<?> routerTask) { if (runnable instanceof RouterTask<?> routerTask) {
RouterCallable routerCallable = (RouterCallable) routerTask.getCallable(); RouterCallable routerCallable = (RouterCallable) routerTask.getCallable();
logger.log(Level.FINEST, "release " + routerCallable); logger.log(Level.FINEST, () -> "releasing " + routerCallable);
routerCallable.release(); routerCallable.release();
} }
} }

View file

@ -34,6 +34,7 @@ import org.xbib.net.http.server.session.OutgoingSessionHandler;
import org.xbib.net.http.server.session.Session; import org.xbib.net.http.server.session.Session;
import org.xbib.net.http.server.session.memory.MemoryPropertiesSessionCodec; import org.xbib.net.http.server.session.memory.MemoryPropertiesSessionCodec;
import org.xbib.net.http.server.validate.HttpRequestValidator; import org.xbib.net.http.server.validate.HttpRequestValidator;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.net.util.NamedThreadFactory; import org.xbib.net.util.NamedThreadFactory;
import org.xbib.net.util.RandomUtil; import org.xbib.net.util.RandomUtil;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
@ -90,6 +91,11 @@ public class BaseApplication implements Application {
return builder.zoneId; return builder.zoneId;
} }
@Override
public MimeTypeService getMimeService() {
return builder.mimeTypeService;
}
public Path getHome() { public Path getHome() {
return builder.home; return builder.home;
} }
@ -147,7 +153,7 @@ public class BaseApplication implements Application {
} }
}; };
Future<?> future = executor.submit(routerCallable); Future<?> future = executor.submit(routerCallable);
logger.log(Level.FINE, "dispatched " + future); logger.log(Level.FINEST, "dispatched " + future);
} }
@Override @Override
@ -169,7 +175,7 @@ public class BaseApplication implements Application {
} }
}; };
Future<?> future = executor.submit(routerCallable); Future<?> future = executor.submit(routerCallable);
logger.log(Level.FINE, "dispatched status " + future); logger.log(Level.FINEST, "dispatched status " + future);
} }
@Override @Override
@ -242,13 +248,13 @@ public class BaseApplication implements Application {
@Override @Override
public void onCreated(Session session) { public void onCreated(Session session) {
logger.log(Level.FINE, "session name = " + sessionName + " created = " + session); logger.log(Level.FINER, "session name = " + sessionName + " created = " + session);
builder.applicationModuleList.forEach(module -> module.onOpen(this, session)); builder.applicationModuleList.forEach(module -> module.onOpen(this, session));
} }
@Override @Override
public void onDestroy(Session session) { public void onDestroy(Session session) {
logger.log(Level.FINE, "session name = " + sessionName + " destroyed = " + session); logger.log(Level.FINER, "session name = " + sessionName + " destroyed = " + session);
builder.applicationModuleList.forEach(module -> module.onClose(this, session)); builder.applicationModuleList.forEach(module -> module.onClose(this, session));
} }
@ -342,7 +348,7 @@ public class BaseApplication implements Application {
((Closeable) outgoingSessionHandler).close(); ((Closeable) outgoingSessionHandler).close();
} }
if (incomingSessionHandler != null && (incomingSessionHandler instanceof Closeable)) { if (incomingSessionHandler != null && (incomingSessionHandler instanceof Closeable)) {
logger.log(Level.FINE, "application closing incming session handler"); logger.log(Level.FINE, "application closing incoming session handler");
((Closeable) incomingSessionHandler).close(); ((Closeable) incomingSessionHandler).close();
} }
if (sessionCodec != null && sessionCodec instanceof Closeable) { if (sessionCodec != null && sessionCodec instanceof Closeable) {

View file

@ -18,6 +18,7 @@ import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams; import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger; import org.xbib.config.SystemConfigLogger;
import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
public class BaseApplicationBuilder implements ApplicationBuilder { public class BaseApplicationBuilder implements ApplicationBuilder {
@ -57,6 +58,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected ZoneId zoneId; protected ZoneId zoneId;
protected MimeTypeService mimeTypeService;
protected Set<String> staticFileSuffixes; protected Set<String> staticFileSuffixes;
protected ConfigParams configParams; protected ConfigParams configParams;
@ -79,6 +82,7 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.sessionsEnabled = true; this.sessionsEnabled = true;
this.locale = Locale.getDefault(); this.locale = Locale.getDefault();
this.zoneId = ZoneId.systemDefault(); this.zoneId = ZoneId.systemDefault();
this.mimeTypeService = new MimeTypeService();
this.applicationModuleList = new ArrayList<>(); this.applicationModuleList = new ArrayList<>();
} }
@ -153,11 +157,19 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this; return this;
} }
@Override
public ApplicationBuilder setMimeTypeService(MimeTypeService mimeTypeService) {
this.mimeTypeService = mimeTypeService;
return this;
}
@Override
public ApplicationBuilder setStaticSuffixes(String... suffixes) { public ApplicationBuilder setStaticSuffixes(String... suffixes) {
this.staticFileSuffixes = Set.of(suffixes); this.staticFileSuffixes = Set.of(suffixes);
return this; return this;
} }
@Override
public ApplicationBuilder registerModule(ApplicationModule applicationModule) { public ApplicationBuilder registerModule(ApplicationModule applicationModule) {
applicationModuleList.add(applicationModule); applicationModuleList.add(applicationModule);
return this; return this;

View file

@ -42,8 +42,8 @@ public class LoginAuthenticationHandler implements HttpHandler {
userProfile = new BaseUserProfile(); userProfile = new BaseUserProfile();
try { try {
authenticate(userProfile, authenticate(userProfile,
(String) context.httpRequest().getParameter().get(userParameterName, Parameter.Domain.DEFAULT, Parameter.Domain.FORM), (String) context.httpRequest().getParameter().get(userParameterName, Parameter.Domain.FORM),
(String) context.httpRequest().getParameter().get(passwordParameterName, Parameter.Domain.DEFAULT, Parameter.Domain.FORM), (String) context.httpRequest().getParameter().get(passwordParameterName, Parameter.Domain.FORM),
context.httpRequest()); context.httpRequest());
context.getAttributes().put("userprofile", userProfile); context.getAttributes().put("userprofile", userProfile);
} catch (Exception e) { } catch (Exception e) {
@ -51,7 +51,7 @@ public class LoginAuthenticationHandler implements HttpHandler {
} }
} }
protected void authenticate(UserProfile userProfile, String username, String password, Request request) throws Exception { protected void authenticate(UserProfile userProfile, String username, String password, Request request) {
if (username == null) { if (username == null) {
logger.log(Level.FINE, "no username given for check, doing nothing"); logger.log(Level.FINE, "no username given for check, doing nothing");
return; return;

View file

@ -54,20 +54,17 @@ public abstract class AbstractResourceHandler implements HttpHandler {
@Override @Override
public void handle(HttpServerContext context) throws IOException { public void handle(HttpServerContext context) throws IOException {
logger.log(Level.FINE, "handle: before creating resource " + this.getClass().getName()); logger.log(Level.FINEST, () -> "handle: before creating resource " + this.getClass().getName());
Resource resource = createResource(context); Resource resource = createResource(context);
logger.log(Level.FINE, "handle: resource = " + (resource != null ? resource.getClass().getName() + " " + resource : null)); logger.log(Level.FINEST, () -> "handle: resource = " + (resource != null ? resource.getClass().getName() + " " + resource : null));
if (resource instanceof HtmlTemplateResource) { if (resource instanceof HtmlTemplateResource) {
logger.log(Level.FINE, "handle: HTML template resource, generate cacheable resource");
generateCacheableResource(context, resource); generateCacheableResource(context, resource);
logger.log(Level.FINE, "handle: done");
return; return;
} }
if (resource == null) { if (resource == null) {
logger.log(Level.FINER, "resource is null: " + resource);
throw new HttpException("resource not found", context, HttpResponseStatus.NOT_FOUND); throw new HttpException("resource not found", context, HttpResponseStatus.NOT_FOUND);
} else if (resource.isDirectory()) { } else if (resource.isDirectory()) {
logger.log(Level.FINER, "we have a directory request"); logger.log(Level.FINEST, "we have a directory request");
if (!resource.getResourcePath().isEmpty() && !resource.getResourcePath().endsWith("/")) { if (!resource.getResourcePath().isEmpty() && !resource.getResourcePath().endsWith("/")) {
URL url = context.request().getBaseURL(); URL url = context.request().getBaseURL();
String loc = url.resolve(resource.getName() + '/') String loc = url.resolve(resource.getName() + '/')
@ -76,59 +73,66 @@ public abstract class AbstractResourceHandler implements HttpHandler {
.fragment(url.getFragment()) .fragment(url.getFragment())
.build() .build()
.toString(); .toString();
logger.log(Level.FINER, "client must add a /, external redirect to = " + loc); logger.log(Level.FINEST, "client must add a /, external redirect to = " + loc);
context.response() context.response()
.addHeader(HttpHeaderNames.LOCATION, loc) .addHeader(HttpHeaderNames.LOCATION, loc)
.setResponseStatus(HttpResponseStatus.TEMPORARY_REDIRECT) // 307 .setResponseStatus(HttpResponseStatus.TEMPORARY_REDIRECT) // 307
.build().flush(); // flush is important .build();
} else if (resource.isExistsIndexFile()) { } else if (resource.isExistsIndexFile()) {
// internal redirect to default index file in this directory // internal redirect to default index file in this directory
logger.log(Level.FINER, "internal redirect to default index file in this directory: " + resource.getIndexFileName()); logger.log(Level.FINEST, "internal redirect to default index file in this directory: " + resource.getIndexFileName());
generateCacheableResource(context, resource); generateCacheableResource(context, resource);
} else { } else {
// send forbidden, we do not allow directory access // send forbidden, we do not allow directory access
context.response() context.response()
.setResponseStatus(HttpResponseStatus.FORBIDDEN) .setResponseStatus(HttpResponseStatus.FORBIDDEN)
.build() .build();
.flush(); // write status
} }
context.done(); context.done();
} else { } else {
logger.log(Level.FINER, "handle: generate cacheable resource");
generateCacheableResource(context, resource); generateCacheableResource(context, resource);
context.done(); context.done();
} }
logger.log(Level.FINER, "handle: done");
} }
private void generateCacheableResource(HttpServerContext context, Resource resource) throws IOException { private void generateCacheableResource(HttpServerContext context,
// if resource is length of 0, there is nothing to send. Do not send any content, Resource resource) throws IOException {
// if resource is length of 0, there is nothing to send. Do not send any content
if (resource.getLength() == 0) { if (resource.getLength() == 0) {
logger.log(Level.FINER, "the resource length is 0, do nothing"); logger.log(Level.FINEST, "the resource length is 0, return not found");
context.response().build().flush(); context.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build();
return; return;
} }
HttpHeaders headers = context.request().getHeaders(); HttpHeaders headers = context.request().getHeaders();
logger.log(Level.FINER, "before generating resource, the response headers are " + context.response().getHeaders()); logger.log(Level.FINEST, () -> "before generating resource, the response headers are " + context.response().getHeaders());
String contentType = resource.getMimeType(); String contentType = resource.getMimeType();
context.response().addHeader(HttpHeaderNames.CONTENT_TYPE, contentType); context.response().addHeader(HttpHeaderNames.CONTENT_TYPE, contentType);
// heuristic for inline disposition // heuristic for inline disposition
String disposition = "inline"; String disposition;
if (!contentType.startsWith("text") && !contentType.startsWith("image")) { if (!contentType.startsWith("text") && !contentType.startsWith("image") && !contentType.startsWith("font")) {
String accept = context.request().getHeaders().get(HttpHeaderNames.ACCEPT); String accept = context.request().getHeaders().get(HttpHeaderNames.ACCEPT);
disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
} else {
disposition = "inline";
} }
if (resource.getBaseName() != null && resource.getSuffix() != null) { if (resource.getBaseName() != null && resource.getSuffix() != null) {
String contentDisposition = disposition + ";filename=\"" + resource.getBaseName() + '.' + resource.getSuffix() + '"';
logger.log(Level.FINEST, () -> "content type = " + contentType + " content disposition = " + contentDisposition);
context.response() context.response()
.addHeader(HttpHeaderNames.CONTENT_DISPOSITION, .addHeader(HttpHeaderNames.CONTENT_DISPOSITION, contentDisposition);
disposition + ";filename=\"" + resource.getBaseName() + '.' + resource.getSuffix() + '"');
} }
long expirationMillis = System.currentTimeMillis() + 1000L * getMaxAgeSeconds(); long expirationMillis = System.currentTimeMillis() + 1000L * getMaxAgeSeconds();
String expires = DateTimeUtil.formatRfc1123(expirationMillis);
if (isCacheResponseEnabled()) { if (isCacheResponseEnabled()) {
String cacheControl = "public, max-age=" + getMaxAgeSeconds();
logger.log(Level.FINEST, () -> "cache response, expires = " + expires + " cache control = " + cacheControl);
context.response() context.response()
.addHeader(HttpHeaderNames.EXPIRES, DateTimeUtil.formatRfc1123(expirationMillis)) .addHeader(HttpHeaderNames.EXPIRES, expires)
.addHeader(HttpHeaderNames.CACHE_CONTROL, "public, max-age=" + getMaxAgeSeconds()); .addHeader(HttpHeaderNames.CACHE_CONTROL, cacheControl);
} else { } else {
logger.log(Level.FINEST, () -> "uncached response");
context.response() context.response()
.addHeader(HttpHeaderNames.EXPIRES, "0") .addHeader(HttpHeaderNames.EXPIRES, "0")
.addHeader(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); .addHeader(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
@ -137,46 +141,53 @@ public abstract class AbstractResourceHandler implements HttpHandler {
if (isETagResponseEnabled()) { if (isETagResponseEnabled()) {
Instant lastModifiedInstant = resource.getLastModified(); Instant lastModifiedInstant = resource.getLastModified();
String eTag = Long.toHexString(resource.getResourcePath().hashCode() + lastModifiedInstant.toEpochMilli() + resource.getLength()); String eTag = Long.toHexString(resource.getResourcePath().hashCode() + lastModifiedInstant.toEpochMilli() + resource.getLength());
logger.log(Level.FINEST, () -> "eTag = " + eTag);
Instant ifUnmodifiedSinceInstant = DateTimeUtil.parseDate(headers.get(HttpHeaderNames.IF_UNMODIFIED_SINCE)); Instant ifUnmodifiedSinceInstant = DateTimeUtil.parseDate(headers.get(HttpHeaderNames.IF_UNMODIFIED_SINCE));
if (ifUnmodifiedSinceInstant != null && if (ifUnmodifiedSinceInstant != null &&
ifUnmodifiedSinceInstant.plusMillis(1000L).isAfter(lastModifiedInstant)) { ifUnmodifiedSinceInstant.plusMillis(1000L).isAfter(lastModifiedInstant)) {
logger.log(Level.FINEST, () -> "precondition failed, lastModified = " + lastModifiedInstant + " ifUnmodifiedSince = " + ifUnmodifiedSinceInstant);
context.response() context.response()
.setResponseStatus(HttpResponseStatus.PRECONDITION_FAILED) .setResponseStatus(HttpResponseStatus.PRECONDITION_FAILED)
.build().flush(); .build();
return; return;
} }
String ifMatch = headers.get(HttpHeaderNames.IF_MATCH); String ifMatch = headers.get(HttpHeaderNames.IF_MATCH);
if (ifMatch != null && !matches(ifMatch, eTag)) { if (ifMatch != null && !matches(ifMatch, eTag)) {
logger.log(Level.FINEST, () -> "precondition failed, ifMatch = " + ifMatch);
context.response() context.response()
.setResponseStatus(HttpResponseStatus.PRECONDITION_FAILED) .setResponseStatus(HttpResponseStatus.PRECONDITION_FAILED)
.build().flush(); .build();
return; return;
} }
String ifNoneMatch = headers.get(HttpHeaderNames.IF_NONE_MATCH); String ifNoneMatch = headers.get(HttpHeaderNames.IF_NONE_MATCH);
if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) { if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
logger.log(Level.FINEST, () -> "not modified, eTag = " + eTag);
context.response() context.response()
.addHeader(HttpHeaderNames.ETAG, eTag) .addHeader(HttpHeaderNames.ETAG, eTag)
.addHeader(HttpHeaderNames.EXPIRES, DateTimeUtil.formatRfc1123(expirationMillis))
.setResponseStatus(HttpResponseStatus.NOT_MODIFIED) .setResponseStatus(HttpResponseStatus.NOT_MODIFIED)
.build().flush(); .build();
return; return;
} }
Instant ifModifiedSinceInstant = DateTimeUtil.parseDate(headers.get(HttpHeaderNames.IF_MODIFIED_SINCE)); Instant ifModifiedSinceInstant = DateTimeUtil.parseDate(headers.get(HttpHeaderNames.IF_MODIFIED_SINCE));
if (ifModifiedSinceInstant != null && if (ifModifiedSinceInstant != null &&
ifModifiedSinceInstant.plusMillis(1000L).isAfter(lastModifiedInstant)) { ifModifiedSinceInstant.plusMillis(1000L).isAfter(lastModifiedInstant)) {
logger.log(Level.FINEST, () -> "not modified (after if-modified-since), eTag = " + eTag);
context.response() context.response()
.addHeader(HttpHeaderNames.ETAG, eTag) .addHeader(HttpHeaderNames.ETAG, eTag)
.addHeader(HttpHeaderNames.EXPIRES, DateTimeUtil.formatRfc1123(expirationMillis))
.setResponseStatus(HttpResponseStatus.NOT_MODIFIED) .setResponseStatus(HttpResponseStatus.NOT_MODIFIED)
.build().flush(); .build();
return; return;
} }
String lastModified = DateTimeUtil.formatRfc1123(lastModifiedInstant);
logger.log(Level.FINEST, () -> "sending resource, lastModified = " + lastModified);
context.response() context.response()
.addHeader(HttpHeaderNames.ETAG, eTag) .addHeader(HttpHeaderNames.ETAG, eTag)
.addHeader(HttpHeaderNames.LAST_MODIFIED, DateTimeUtil.formatRfc1123(lastModifiedInstant)); .addHeader(HttpHeaderNames.LAST_MODIFIED, lastModified);
if (isRangeResponseEnabled()) { if (isRangeResponseEnabled()) {
performRangeResponse(context, resource, contentType, eTag, headers); performRangeResponse(context, resource, contentType, eTag, headers);
sent = true; sent = true;
} else {
logger.log(Level.WARNING, "range response not enabled");
} }
} }
if (!sent) { if (!sent) {
@ -185,18 +196,23 @@ public abstract class AbstractResourceHandler implements HttpHandler {
String string = Long.toString(resource.getLength()); String string = Long.toString(resource.getLength());
context.response() context.response()
.addHeader(HttpHeaderNames.CONTENT_LENGTH, string); .addHeader(HttpHeaderNames.CONTENT_LENGTH, string);
logger.log(Level.FINEST, "length is known = " + resource.getLength());
send(resource, HttpResponseStatus.OK, contentType, context, 0L, resource.getLength()); send(resource, HttpResponseStatus.OK, contentType, context, 0L, resource.getLength());
} else { } else {
logger.log(Level.FINEST, "length is unknown");
send(resource, HttpResponseStatus.OK, contentType, context, 0L, -1L); send(resource, HttpResponseStatus.OK, contentType, context, 0L, -1L);
} }
} }
logger.log(Level.FINEST, "generation done");
} }
private void performRangeResponse(HttpServerContext context, private void performRangeResponse(HttpServerContext context,
Resource resource, Resource resource,
String contentType, String eTag, String contentType,
HttpHeaders headers) throws IOException { String eTag,
HttpHeaders headers) throws IOException {
long length = resource.getLength(); long length = resource.getLength();
logger.log(Level.FINEST, "performing range response on resource = " + resource);
context.response().addHeader(HttpHeaderNames.ACCEPT_RANGES, "bytes"); context.response().addHeader(HttpHeaderNames.ACCEPT_RANGES, "bytes");
Range full = new Range(0, length - 1, length); Range full = new Range(0, length - 1, length);
List<Range> ranges = new ArrayList<>(); List<Range> ranges = new ArrayList<>();
@ -293,19 +309,21 @@ public abstract class AbstractResourceHandler implements HttpHandler {
HttpResponseStatus httpResponseStatus, HttpResponseStatus httpResponseStatus,
String contentType, String contentType,
HttpServerContext context, HttpServerContext context,
long offset, long size) throws IOException { long offset,
long size) throws IOException {
if (resource instanceof HttpServerResource) { if (resource instanceof HttpServerResource) {
logger.log(Level.FINER, "we have a server resource, render by resource"); logger.log(Level.FINEST, "let server resource render");
((HttpServerResource) resource).render(context); ((HttpServerResource) resource).render(context);
return; return;
} }
URL url = resource.getURL(); URL url = resource.getURL();
logger.log(Level.FINEST, "sending URL = " + url + " offset = " + offset + " size = " + size);
if (url == null) { if (url == null) {
logger.log(Level.WARNING, "url is null, generating not found");
context.response() context.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND) .setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build(); .build();
} else if (context.request().getMethod() == HttpMethod.HEAD) { } else if (context.request().getMethod() == HttpMethod.HEAD) {
logger.log(Level.FINEST, "HEAD request, do not send body");
context.response() context.response()
.setResponseStatus(HttpResponseStatus.OK) .setResponseStatus(HttpResponseStatus.OK)
.setContentType(contentType) .setContentType(contentType)
@ -323,9 +341,16 @@ public abstract class AbstractResourceHandler implements HttpHandler {
} }
} else { } else {
try (InputStream inputStream = url.openStream()) { try (InputStream inputStream = url.openStream()) {
send(inputStream, httpResponseStatus, contentType, context.response(), offset, size); if (inputStream != null) {
send(inputStream, httpResponseStatus, contentType, context.response(), offset, size);
} else {
logger.log(Level.WARNING, "input stream is null, url = " + url);
context.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build();
}
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e); logger.log(Level.SEVERE, e.getMessage() + " url=" + url, e);
context.response() context.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND) .setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build(); .build();
@ -357,7 +382,8 @@ public abstract class AbstractResourceHandler implements HttpHandler {
HttpResponseStatus httpResponseStatus, HttpResponseStatus httpResponseStatus,
String contentType, String contentType,
HttpResponseBuilder responseBuilder, HttpResponseBuilder responseBuilder,
long offset, long size) throws IOException { long offset,
long size) throws IOException {
if (inputStream == null) { if (inputStream == null) {
logger.log(Level.WARNING, "inputstream is null, generating not found"); logger.log(Level.WARNING, "inputstream is null, generating not found");
responseBuilder.setResponseStatus(HttpResponseStatus.NOT_FOUND).build(); responseBuilder.setResponseStatus(HttpResponseStatus.NOT_FOUND).build();

View file

@ -53,7 +53,7 @@ public class HtmlTemplateResource implements HttpServerResource {
} }
this.resourcePath = httpServerContext.request().getRequestPath().substring(1); this.resourcePath = httpServerContext.request().getRequestPath().substring(1);
this.path = resourcePath.length() > 0 ? root.resolve(resourcePath) : root; this.path = resourcePath.length() > 0 ? root.resolve(resourcePath) : root;
logger.log(Level.FINER, "class = " + getClass().getName() + logger.log(Level.FINEST, "class = " + getClass().getName() +
" root = " + root + " root = " + root +
" resource path = " + resourcePath + " resource path = " + resourcePath +
" path = " + path + " path = " + path +
@ -64,9 +64,9 @@ public class HtmlTemplateResource implements HttpServerResource {
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {
if (getIndexFileName() != null) { if (getIndexFileName() != null) {
Path indexPath = path.resolve(indexFileName); Path indexPath = path.resolve(indexFileName);
logger.log(Level.FINE, "resolved to index path = " + indexPath); logger.log(Level.FINEST, "resolved to index path = " + indexPath);
if (Files.exists(indexPath)) { if (Files.exists(indexPath)) {
logger.log(Level.FINE, "index path exists"); logger.log(Level.FINEST, "index path exists");
this.isExistsIndexFile = true; this.isExistsIndexFile = true;
this.path = indexPath; this.path = indexPath;
this.isDirectory = false; this.isDirectory = false;
@ -84,7 +84,7 @@ public class HtmlTemplateResource implements HttpServerResource {
} }
this.isExists = Files.exists(path); this.isExists = Files.exists(path);
this.url = URL.create(path.toUri().toString()); this.url = URL.create(path.toUri().toString());
logger.log(Level.FINE, "isExists = " + isExists + " isDirectory = " + isDirectory + " url = " + url); logger.log(Level.FINEST, () -> "isExists = " + isExists + " isDirectory = " + isDirectory + " url = " + url);
this.lastModified = isExists ? Files.getLastModifiedTime(path).toInstant() : Instant.now(); this.lastModified = isExists ? Files.getLastModifiedTime(path).toInstant() : Instant.now();
// length will be computed at rendering time // length will be computed at rendering time
this.length = -1; this.length = -1;

View file

@ -107,7 +107,7 @@ public class IncomingSessionHandler implements HttpHandler {
} }
} }
context.getAttributes().put("session", session); context.getAttributes().put("session", session);
logger.log(Level.FINER, "incoming session " + session.id()); logger.log(Level.FINEST, "incoming session " + session.id());
} }
private Map<String, Object> decodeCookie(Cookie cookie) throws IOException, private Map<String, Object> decodeCookie(Cookie cookie) throws IOException,

View file

@ -84,7 +84,7 @@ public class OutgoingSessionHandler implements HttpHandler {
} }
String suffix = SessionUtil.extractExtension(context.request().getRequestPath()); String suffix = SessionUtil.extractExtension(context.request().getRequestPath());
if (suffix != null && suffixes.contains(suffix)) { if (suffix != null && suffixes.contains(suffix)) {
logger.log(Level.FINE, "suffix " + suffix + " blocking outgoing session handling"); logger.log(Level.FINEST, () -> "suffix " + suffix + " blocking outgoing session handling");
return; return;
} }
CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "outgoingcookies"); CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "outgoingcookies");
@ -105,7 +105,7 @@ public class OutgoingSessionHandler implements HttpHandler {
if (session != null) { if (session != null) {
try { try {
if (userProfile != null) { if (userProfile != null) {
logger.log(Level.FINE, "user profile present: " + userProfile); logger.log(Level.FINEST, () -> "user profile present: " + userProfile);
if (sessionUserName != null) { if (sessionUserName != null) {
session.put(sessionUserName, userProfile.getUserId()); session.put(sessionUserName, userProfile.getUserId());
} }
@ -123,7 +123,7 @@ public class OutgoingSessionHandler implements HttpHandler {
throw new HttpException("unable to create session cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); throw new HttpException("unable to create session cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
} }
} }
logger.log(Level.FINER, "outgoing cookies = " + cookieBox); logger.log(Level.FINEST, "outgoing cookies = " + cookieBox);
context.getAttributes().put("outgoingcookies", cookieBox); context.getAttributes().put("outgoingcookies", cookieBox);
} }

View file

@ -121,7 +121,7 @@ public abstract class DefaultMarkupTemplate extends BaseTemplate {
} }
if (request != null) { if (request != null) {
URL url = request.getServerURL().resolve(prefix).resolve(rel); URL url = request.getServerURL().resolve(prefix).resolve(rel);
logger.log(Level.FINE, "server base URL = " + request.getServerURL() + logger.log(Level.FINEST, "server base URL = " + request.getServerURL() +
" prefix = " + prefix + " rel = " + rel + " --> " + url); " prefix = " + prefix + " rel = " + rel + " --> " + url);
return absolute ? url.toExternalForm() : toOrigin(url); return absolute ? url.toExternalForm() : toOrigin(url);
} else { } else {

View file

@ -41,7 +41,7 @@ public class DefaultTemplateResolver implements TemplateResolver {
String templateResourceString = languageTag != null ? String templateResourceString = languageTag != null ?
templateResource.withLocale(languageTag).toString() : templateResource.withLocale(languageTag).toString() :
templateResource.toString(); templateResource.toString();
logger.log(Level.FINER, "template resource string = " + templateResourceString + " locale = " + locale logger.log(Level.FINEST, "template resource string = " + templateResourceString + " locale = " + locale
+ " templateConfiguration.getLocale() = " + templateConfiguration.getLocale() + " languageTag = " + languageTag); + " templateConfiguration.getLocale() = " + templateConfiguration.getLocale() + " languageTag = " + languageTag);
Path path = resolver.resolve(templateResourceString); Path path = resolver.resolve(templateResourceString);
return path.toUri().toURL(); return path.toUri().toURL();

View file

@ -31,17 +31,16 @@ class GroovyHttpResonseStatusTemplateResource extends GroovyTemplateResource {
@Override @Override
public void render(HttpServerContext httpServerContext) throws IOException { public void render(HttpServerContext httpServerContext) throws IOException {
logger.log(Level.FINE, "rendering HTTP status by Groovy"); logger.log(Level.FINEST, "rendering HTTP status by Groovy");
httpServerContext.getAttributes().put("_status", httpResponseStatus); httpServerContext.getAttributes().put("_status", httpResponseStatus);
httpServerContext.getAttributes().put("_message", message); httpServerContext.getAttributes().put("_message", message);
httpServerContext.getAttributes().put("_resource", this); httpServerContext.getAttributes().put("_resource", this);
Application application = httpServerContext.getAttributes().get(Application.class, "application"); Application application = httpServerContext.getAttributes().get(Application.class, "application");
GroovyMarkupTemplateHandler groovyMarkupTemplateHandler = new GroovyMarkupTemplateHandler(application); GroovyMarkupTemplateHandler groovyMarkupTemplateHandler = new GroovyMarkupTemplateHandler(application);
logger.log(Level.FINE, "handle groovyMarkupTemplateHandler"); logger.log(Level.FINEST, "handle groovyMarkupTemplateHandler");
groovyMarkupTemplateHandler.handle(httpServerContext); groovyMarkupTemplateHandler.handle(httpServerContext);
super.render(httpServerContext); super.render(httpServerContext);
GroovyTemplateRenderer groovyTemplateRenderer = new GroovyTemplateRenderer(); GroovyTemplateRenderer groovyTemplateRenderer = new GroovyTemplateRenderer();
logger.log(Level.FINE, "handle groovyTemplateRenderer");
groovyTemplateRenderer.handle(httpServerContext); groovyTemplateRenderer.handle(httpServerContext);
} }

View file

@ -29,9 +29,9 @@ public class GroovyTemplateRenderer implements HttpHandler {
HttpResponseStatus httpResponseStatus = context.getAttributes().get(HttpResponseStatus.class, "_status", HttpResponseStatus.OK); HttpResponseStatus httpResponseStatus = context.getAttributes().get(HttpResponseStatus.class, "_status", HttpResponseStatus.OK);
context.response() context.response()
.setResponseStatus(httpResponseStatus) .setResponseStatus(httpResponseStatus)
.setHeader("cache-control", "no-cache") // override default must-revalidate behavior
.setHeader("content-length", Integer.toString(dataBuffer.writePosition())) .setHeader("content-length", Integer.toString(dataBuffer.writePosition()))
.setContentType("text/html; charset=" + StandardCharsets.UTF_8.displayName()) .setContentType("text/html; charset=" + StandardCharsets.UTF_8.displayName())
.setHeader("cache-control", "no-cache")
.write(dataBuffer); .write(dataBuffer);
} }
} }

View file

@ -47,7 +47,7 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
@Override @Override
public void render(HttpServerContext httpServerContext) throws IOException { public void render(HttpServerContext httpServerContext) throws IOException {
logger.log(Level.FINER, "rendering groovy template, path = " + getPath() + " isExists = " + isExists() + " isDirectory =" + isDirectory() ); logger.log(Level.FINEST, () -> "rendering groovy template, path = " + getPath() + " isExists = " + isExists() + " isDirectory =" + isDirectory() );
Application application = httpServerContext.getAttributes().get(Application.class, "application"); Application application = httpServerContext.getAttributes().get(Application.class, "application");
if (application == null) { if (application == null) {
logger.log(Level.WARNING, "application is null"); logger.log(Level.WARNING, "application is null");
@ -63,9 +63,9 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
if (service instanceof GroovyTemplateService groovyTemplateService) { if (service instanceof GroovyTemplateService groovyTemplateService) {
if (groovyTemplateService.getTemplateName() != null) { if (groovyTemplateService.getTemplateName() != null) {
templatePath = application.resolve(groovyTemplateService.getTemplateName()); templatePath = application.resolve(groovyTemplateService.getTemplateName());
logger.log(Level.FINER, "templatePath after application.resolve() = " + templatePath); logger.log(Level.FINEST, "templatePath after application.resolve() = " + templatePath);
} else { } else {
logger.log(Level.FINER, "the GroovyTemplateService does not have a templateName"); logger.log(Level.FINEST, "the GroovyTemplateService does not have a templateName");
} }
} }
// status response handlers have priority // status response handlers have priority
@ -75,17 +75,17 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
if (indexFileName != null) { if (indexFileName != null) {
templatePath = application.resolve(indexFileName); templatePath = application.resolve(indexFileName);
} }
logger.log(Level.FINER, "rendering Groovy HTTP status response with templatePath = " + templatePath); logger.log(Level.FINEST, "rendering Groovy HTTP status response with templatePath = " + templatePath);
} else { } else {
// override if 'templatePath' attribute is set // override if 'templatePath' attribute is set
String overridePath = httpServerContext.getAttributes().get(String.class, "templatePath"); String overridePath = httpServerContext.getAttributes().get(String.class, "templatePath");
if (overridePath != null) { if (overridePath != null) {
logger.log(Level.FINER, "found override templatePath = " + overridePath); logger.log(Level.FINEST, "found override templatePath = " + overridePath);
templatePath = application.resolve(overridePath); templatePath = application.resolve(overridePath);
logger.log(Level.FINER, "found override templatePath, resolved to " + templatePath); logger.log(Level.FINEST, "found override templatePath, resolved to " + templatePath);
} }
if (templatePath == null) { if (templatePath == null) {
logger.log(Level.FINER, "templatePath is null, OOTB effort on " + getIndexFileName()); logger.log(Level.FINEST, "templatePath is null, OOTB effort on " + getIndexFileName());
// OOTB rendering via getIndexFileName(), no getPath(), no getTemplateName() // OOTB rendering via getIndexFileName(), no getPath(), no getTemplateName()
templatePath = application.resolve(getIndexFileName()); templatePath = application.resolve(getIndexFileName());
} }
@ -104,7 +104,7 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
} }
templates.computeIfAbsent(templatePath, path -> { templates.computeIfAbsent(templatePath, path -> {
try { try {
logger.log(Level.FINEST, "groovy templatePath = " + path + " creating by template engine"); logger.log(Level.FINEST, () -> "groovy templatePath = " + path + " creating by template engine");
return templateEngine.createTemplate(Files.readString(path)); return templateEngine.createTemplate(Files.readString(path));
} catch (Exception e) { } catch (Exception e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
@ -132,7 +132,7 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
return; return;
} }
// handle programmatic locale change plus template making under lock so no other request/response can interrupt us // handle programmatic locale change plus template making under lock so no other request/response can interrupt us
logger.log(Level.FINER, "application locale for template = " + application.getLocale()); logger.log(Level.FINEST, () -> "application locale for template = " + application.getLocale());
try { try {
lock.lock(); lock.lock();
templateResolver.setLocale(application.getLocale()); templateResolver.setLocale(application.getLocale());
@ -141,7 +141,7 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
if (acceptLanguage != null) { if (acceptLanguage != null) {
Locale negotiatedLocale = LocaleNegotiator.findLocale(acceptLanguage); Locale negotiatedLocale = LocaleNegotiator.findLocale(acceptLanguage);
if (negotiatedLocale != null) { if (negotiatedLocale != null) {
logger.log(Level.FINER, "negotiated locale for template = " + negotiatedLocale); logger.log(Level.FINEST, () -> "negotiated locale for template = " + negotiatedLocale);
templateResolver.setLocale(negotiatedLocale); templateResolver.setLocale(negotiatedLocale);
} }
} }