Skip to content

Commit

Permalink
Static content update (#6195)
Browse files Browse the repository at this point in the history
- renamed support to service (as it is an HttpService)
- added support for single file static content handler for file system (works on classpath)
  • Loading branch information
tomas-langer authored Feb 14, 2023
1 parent 150c037 commit 472413f
Show file tree
Hide file tree
Showing 17 changed files with 229 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,7 @@
import io.helidon.common.http.Http.Header;
import io.helidon.nima.webserver.WebServer;
import io.helidon.nima.webserver.http.HttpRules;
import io.helidon.nima.webserver.staticcontent.StaticContentSupport;
import io.helidon.nima.webserver.staticcontent.StaticContentService;

/**
* This application provides a simple file upload service with a UI to exercise multipart.
Expand Down Expand Up @@ -56,7 +56,7 @@ static void routing(HttpRules rules) {
res.header(UI_LOCATION);
res.send();
})
.register("/ui", StaticContentSupport.builder("WEB")
.register("/ui", StaticContentService.builder("WEB")
.welcomeFileName("index.html")
.build())
.register("/api", new FileService());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,7 @@

import io.helidon.nima.webserver.WebServer;
import io.helidon.nima.webserver.http.HttpRouting;
import io.helidon.nima.webserver.staticcontent.StaticContentSupport;
import io.helidon.nima.webserver.staticcontent.StaticContentService;

/**
* Static content example.
Expand All @@ -45,7 +45,7 @@ public static void main(String[] args) {
static void routing(HttpRouting.Builder routing) {
// register static content on root path of the server
// use classpath /web to look for resources
routing.register("/", StaticContentSupport.builder("web"))
routing.register("/", StaticContentService.builder("web"))
.get("/api/greet", (req, res) -> res.send("Hello World!"));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import io.helidon.nima.webserver.context.ContextFeature;
import io.helidon.nima.webserver.http.HttpRouting;
import io.helidon.nima.webserver.http.HttpService;
import io.helidon.nima.webserver.staticcontent.StaticContentSupport;
import io.helidon.nima.webserver.staticcontent.StaticContentService;

import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
Expand Down Expand Up @@ -421,14 +421,14 @@ private void registerStaticContent() {

private void registerPathStaticContent(Config config) {
Config context = config.get("context");
StaticContentSupport.FileSystemBuilder pBuilder = StaticContentSupport.builder(config.get("location")
StaticContentService.FileSystemBuilder pBuilder = StaticContentService.builder(config.get("location")
.as(Path.class)
.get());
pBuilder.welcomeFileName(config.get("welcome")
.asString()
.orElse("index.html"));

StaticContentSupport staticContent = pBuilder.build();
StaticContentService staticContent = pBuilder.build();

if (context.exists()) {
routingBuilder.register(context.asString().get(), staticContent);
Expand All @@ -441,7 +441,7 @@ private void registerPathStaticContent(Config config) {
private void registerClasspathStaticContent(Config config) {
Config context = config.get("context");

StaticContentSupport.ClassPathBuilder cpBuilder = StaticContentSupport.builder(config.get("location").asString().get());
StaticContentService.ClassPathBuilder cpBuilder = StaticContentService.builder(config.get("location").asString().get());
cpBuilder.welcomeFileName(config.get("welcome")
.asString()
.orElse("index.html"));
Expand All @@ -455,7 +455,7 @@ private void registerClasspathStaticContent(Config config) {
.flatMap(List::stream)
.forEach(cpBuilder::addCacheInMemory);

StaticContentSupport staticContent = cpBuilder.build();
StaticContentService staticContent = cpBuilder.build();

if (context.exists()) {
routingBuilder.register(context.asString().get(), staticContent);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,7 +22,7 @@
import io.helidon.nima.webclient.http1.Http1Client;
import io.helidon.nima.webclient.http1.Http1ClientResponse;
import io.helidon.nima.webserver.http.HttpRouting;
import io.helidon.nima.webserver.staticcontent.StaticContentSupport;
import io.helidon.nima.webserver.staticcontent.StaticContentService;

import org.junit.jupiter.api.Test;

Expand All @@ -39,7 +39,7 @@ class StaticContentTest {

@SetUpRoute
static void routing(HttpRouting.Builder routing) {
routing.register("/files", StaticContentSupport.builder("static")
routing.register("/files", StaticContentService.builder("static")
.welcomeFileName("welcome.txt")
.build())
.get("/files/default", (req, res) -> res.send("Nexted"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2022 Oracle and/or its affiliates.
* Copyright (c) 2017, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -61,7 +61,7 @@ class ClassPathContentHandler extends FileBasedContentHandler {
// URL's hash code and equal are not suitable for map or set
private final Map<String, ExtractedJarEntry> extracted = new ConcurrentHashMap<>();

ClassPathContentHandler(StaticContentSupport.ClassPathBuilder builder) {
ClassPathContentHandler(StaticContentService.ClassPathBuilder builder) {
super(builder);

this.classLoader = builder.classLoader();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022 Oracle and/or its affiliates.
* Copyright (c) 2021, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,7 +43,7 @@
abstract class FileBasedContentHandler extends StaticContentHandler {
private final Map<String, MediaType> customMediaTypes;

FileBasedContentHandler(StaticContentSupport.FileBasedBuilder<?> builder) {
FileBasedContentHandler(StaticContentService.FileBasedBuilder<?> builder) {
super(builder);

this.customMediaTypes = builder.specificContentTypes();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2022 Oracle and/or its affiliates.
* Copyright (c) 2017, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,6 @@

import java.io.IOException;
import java.lang.System.Logger.Level;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
Expand All @@ -41,7 +40,7 @@ class FileSystemContentHandler extends FileBasedContentHandler {
private final Path root;
private final Set<String> cacheInMemory;

FileSystemContentHandler(StaticContentSupport.FileSystemBuilder builder) {
FileSystemContentHandler(StaticContentService.FileSystemBuilder builder) {
super(builder);

this.root = builder.root().toAbsolutePath().normalize();
Expand Down Expand Up @@ -148,7 +147,7 @@ boolean doHandle(Http.Method method,
return handler.handle(handlerCache(), method, req, res, requestedResource);
}

private void addToInMemoryCache(String resource) throws IOException, URISyntaxException {
private void addToInMemoryCache(String resource) throws IOException {
/*
we need to know:
- content size
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.nima.webserver.staticcontent;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;

import io.helidon.common.http.Http;
import io.helidon.common.http.ServerResponseHeaders;
import io.helidon.nima.webserver.http.ServerRequest;
import io.helidon.nima.webserver.http.ServerResponse;

class SingleFileContentHandler extends FileBasedContentHandler {
private static final System.Logger LOGGER = System.getLogger(SingleFileContentHandler.class.getName());

private final boolean cacheInMemory;
private final Path path;

SingleFileContentHandler(FileSystemBuilder builder) {
super(builder);

this.cacheInMemory = builder.cacheInMemory().contains(".") || builder.cacheInMemory().contains("/");
this.path = builder.root().toAbsolutePath().normalize();
}

@Override
public void beforeStart() {
try {
if (cacheInMemory) {
// directly cache in memory
byte[] fileBytes = Files.readAllBytes(path);
cacheInMemory(".", detectType(fileName(path)), fileBytes, lastModified(path));
} else {
// cache a handler that loads it from file system
cacheFileHandler();
}
} catch (IOException e) {
LOGGER.log(System.Logger.Level.WARNING, "Failed to add file to in-memory cache, path: " + path, e);
}
super.beforeStart();
}

@Override
boolean doHandle(Http.Method method, String requestedPath, ServerRequest req, ServerResponse res) throws IOException {
if ("".equals(requestedPath) || "/".equals(requestedPath)) {
Optional<CachedHandler> cachedHandler = cacheHandler(".");
if (cachedHandler.isPresent()) {
return cachedHandler.get().handle(handlerCache(), method, req, res, requestedPath);
}
return doHandle(method, req, res);
}

if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
LOGGER.log(System.Logger.Level.DEBUG, "Requested sub-path for a single file static content: " + requestedPath);
}
return false;
}

private boolean doHandle(Http.Method method, ServerRequest req, ServerResponse res) throws IOException {
return cacheFileHandler().handle(handlerCache(), method, req, res, ".");
}

private CachedHandler cacheFileHandler() {
CachedHandler handler = new CachedHandlerPath(path,
detectType(fileName(path)),
FileBasedContentHandler::lastModified,
ServerResponseHeaders::lastModified);
cacheHandler(".", handler);

return handler;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2022 Oracle and/or its affiliates.
* Copyright (c) 2017, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -48,7 +48,7 @@
/**
* Base implementation of static content support.
*/
abstract class StaticContentHandler implements StaticContentSupport {
abstract class StaticContentHandler implements StaticContentService {
private static final System.Logger LOGGER = System.getLogger(StaticContentHandler.class.getName());

private final Map<String, CachedHandlerInMemory> inMemoryCache = new ConcurrentHashMap<>();
Expand All @@ -57,7 +57,7 @@ abstract class StaticContentHandler implements StaticContentSupport {
private final Function<String, String> resolvePathFunction;
private final AtomicInteger webServerCounter = new AtomicInteger();

StaticContentHandler(StaticContentSupport.Builder<?> builder) {
StaticContentHandler(StaticContentService.Builder<?> builder) {
this.welcomeFilename = builder.welcomeFileName();
this.resolvePathFunction = builder.resolvePathFunction();
this.handlerCache = builder.handlerCache();
Expand Down
Loading

0 comments on commit 472413f

Please sign in to comment.