Skip to content

Commit

Permalink
🎨 improve static file handler
Browse files Browse the repository at this point in the history
  • Loading branch information
hellokaton committed May 9, 2022
1 parent daceb0e commit 2f1c463
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 141 deletions.
83 changes: 37 additions & 46 deletions blade-core/src/main/java/com/hellokaton/blade/Blade.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@
import com.hellokaton.blade.mvc.hook.WebHook;
import com.hellokaton.blade.mvc.http.HttpMethod;
import com.hellokaton.blade.mvc.http.session.SessionManager;
import com.hellokaton.blade.mvc.route.DynamicMapping;
import com.hellokaton.blade.mvc.route.RouteMatcher;
import com.hellokaton.blade.mvc.route.mapping.dynamic.RegexMapping;
import com.hellokaton.blade.mvc.route.mapping.dynamic.TrieMapping;
import com.hellokaton.blade.mvc.ui.template.DefaultEngine;
import com.hellokaton.blade.mvc.ui.template.TemplateEngine;
import com.hellokaton.blade.options.CorsOptions;
import com.hellokaton.blade.options.HttpOptions;
import com.hellokaton.blade.options.StaticOptions;
import com.hellokaton.blade.server.NettyServer;
import com.hellokaton.blade.server.Server;
import lombok.AccessLevel;
Expand All @@ -54,6 +52,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.hellokaton.blade.mvc.BladeConst.ENV_KEY_FAVICON_DIR;

/**
* Blade Core
* <p>
Expand All @@ -78,13 +78,6 @@ public class Blade {
*/
private final Set<String> packages = new LinkedHashSet<>(BladeConst.PLUGIN_PACKAGE_NAME);

/**
* All static resource URL prefixes,
* defaults to "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/",
* which are located under classpath
*/
private final Set<String> statics = new HashSet<>(BladeConst.DEFAULT_STATICS);

/**
* The default IOC container implementation
*/
Expand Down Expand Up @@ -121,7 +114,8 @@ public class Blade {
private final RouteMatcher routeMatcher = new RouteMatcher();

private CorsOptions corsOptions = null;
private final HttpOptions httpOptions = HttpOptions.create();
private HttpOptions httpOptions = HttpOptions.create();
private StaticOptions staticOptions = StaticOptions.create();

/**
* Blade environment, which stores the parameters of the application.properties configuration file
Expand Down Expand Up @@ -280,6 +274,17 @@ public TemplateEngine templateEngine() {
return this.templateEngine;
}

/**
* setting favicon dir, default is /static
*
* @param faviconDir favicon dir
* @return blade instance
*/
public Blade faviconDir(String faviconDir) {
this.setEnv(ENV_KEY_FAVICON_DIR, faviconDir);
return this;
}

/**
* Get RouteMatcher
*
Expand All @@ -294,6 +299,21 @@ public Blade http(Consumer<HttpOptions> consumer) {
return this;
}

public Blade http(HttpOptions httpOptions) {
this.httpOptions = httpOptions;
return this;
}

public Blade staticOptions(Consumer<StaticOptions> consumer) {
consumer.accept(this.staticOptions);
return this;
}

public Blade staticOptions(StaticOptions staticOptions) {
this.staticOptions = staticOptions;
return this;
}

public Blade cors(CorsOptions corsOptions) {
this.corsOptions = corsOptions;
return this;
Expand All @@ -307,6 +327,10 @@ public HttpOptions httpOptions() {
return this.httpOptions;
}

public StaticOptions staticOptions() {
return this.staticOptions;
}

/**
* Register bean to ioc container
*
Expand All @@ -329,29 +353,6 @@ public Blade register(@NonNull Class<?> cls) {
return this;
}

/**
* Add multiple static resource file
* the default provides the static, upload
*
* @param folders static resource directory
* @return blade
*/
public Blade addStatics(@NonNull String... folders) {
this.statics.addAll(Arrays.asList(folders));
return this;
}

/**
* Set whether to show the file directory, default doesn't show
*
* @param fileList show the file directory
* @return blade
*/
public Blade showFileList(boolean fileList) {
this.environment.set(BladeConst.ENV_KEY_STATIC_LIST, fileList);
return this;
}

/**
* Get ioc bean
*
Expand Down Expand Up @@ -415,16 +416,6 @@ public Class<?> bootClass() {
return this.bootClass;
}

/**
* Get blade statics list.
* e.g: "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/"
*
* @return return statics
*/
public Set<String> getStatics() {
return this.statics;
}

/**
* When set to start blade scan packages
*
Expand Down Expand Up @@ -686,8 +677,8 @@ public Blade start(Class<?> mainCls, String... args) {
try {
//TODO: add support for Create and Delete
if (event.equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
Path destPath = FileChangeDetector.getDestPath(filePath, environment);
Files.copy(filePath, destPath, StandardCopyOption.REPLACE_EXISTING);
Path destPath = FileChangeDetector.getDestPath(filePath, environment, staticOptions);
Files.copy(filePath, Objects.requireNonNull(destPath), StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
log.error("Exception when trying to copy updated file");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import com.hellokaton.blade.Environment;
import com.hellokaton.blade.mvc.BladeConst;
import com.hellokaton.blade.options.StaticOptions;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

Expand All @@ -19,18 +23,18 @@ public class FileChangeDetector {
private final WatchService watcher;
private final Map<WatchKey, Path> pathMap = new HashMap<>();

public FileChangeDetector(String dirPath) throws IOException{
public FileChangeDetector(String dirPath) throws IOException {
watcher = FileSystems.getDefault().newWatchService();
registerAll(Paths.get(dirPath));
}

private void register(Path dir) throws IOException{
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
pathMap.put(key,dir);
pathMap.put(key, dir);
}

private void registerAll(Path dir) throws IOException{
Files.walkFileTree(dir, new SimpleFileVisitor<Path>(){
private void registerAll(Path dir) throws IOException {
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
register(dir);
Expand All @@ -39,46 +43,41 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
});
}

public void processEvent(BiConsumer<WatchEvent.Kind<Path>, Path> processor){
for(;;){
public void processEvent(BiConsumer<WatchEvent.Kind<Path>, Path> processor) {
for (; ; ) {
WatchKey key;
try{
try {
key = watcher.take();
}catch (InterruptedException e) {
} catch (InterruptedException e) {
return;
}

Path dir = pathMap.get(key);
for (WatchEvent<?> event: key.pollEvents()){
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
Path filePath = dir.resolve(((WatchEvent<Path>)event).context());
Path filePath = dir.resolve(((WatchEvent<Path>) event).context());

if(Files.isDirectory(filePath)) continue;
log.info("File {} changes detected!",filePath.toString());
if (Files.isDirectory(filePath)) continue;
log.info("File {} changes detected!", filePath.toString());
//copy updated files to target
processor.accept(kind, filePath);
}
key.reset();
}
}

public static Path getDestPath(Path src, Environment env){
String templateDir = env.get(BladeConst.ENV_KEY_TEMPLATE_PATH,"/templates");
Optional<String> staticDir = env.get(BladeConst.ENV_KEY_STATIC_DIRS);
public static Path getDestPath(Path src, Environment env, StaticOptions staticOptions) {
String templateDir = env.get(BladeConst.ENV_KEY_TEMPLATE_PATH, "/templates");
List<String> templateOrStaticDirKeyword = new ArrayList<>();
templateOrStaticDirKeyword.add(templateDir);
if(staticDir.isPresent()){
templateOrStaticDirKeyword.addAll(Arrays.asList(staticDir.get().split(",")));
}else{
templateOrStaticDirKeyword.addAll(BladeConst.DEFAULT_STATICS);
}
templateOrStaticDirKeyword.addAll(staticOptions.getPaths());

List result = templateOrStaticDirKeyword.stream().filter(dir -> src.toString().indexOf(dir)!=-1).collect(Collectors.toList());
if(result.size()!=1){
List<String> result = templateOrStaticDirKeyword.stream().filter(dir -> src.toString().contains(dir)).collect(Collectors.toList());
if (result.size() != 1) {
log.info("Cannot get dest dir");
return null;
return null;
}
String key = (String)result.get(0);
String key = result.get(0);
log.info(BladeConst.CLASSPATH + src.toString().substring(src.toString().indexOf(key)));
return Paths.get(BladeConst.CLASSPATH + src.toString().substring(src.toString().indexOf(key)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import com.hellokaton.blade.kit.StringKit;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

Expand All @@ -38,8 +37,6 @@ public interface BladeConst {
String INTERNAL_SERVER_ERROR_HTML = "<center><h1>500 Internal Server Error</h1><hr/></center>";
String DEFAULT_THREAD_NAME = "_(:3」∠)_";
List<String> PLUGIN_PACKAGE_NAME = new ArrayList<>(Collections.singletonList("com.hellokaton.blade.plugin"));
List<String> DEFAULT_STATICS = new ArrayList<>(
Arrays.asList("/favicon.ico", "/robots.txt", "/static", "/upload", "/webjars/"));

String PROP_NAME = "classpath:application.properties";

Expand All @@ -53,17 +50,17 @@ public interface BladeConst {
String ENV_KEY_TASK_THREAD_COUNT = "app.task.thread-count";
String ENV_KEY_CONTEXT_PATH = "app.context-path";
String ENV_KEY_REQUEST_LOG = "app.request-log";
String ENV_KEY_FAVICON_DIR = "app.favicon-dir";
String ENV_KEY_HTTP_MAX_CONTENT = "http.max-content-size";
String ENV_KEY_GZIP_ENABLE = "http.gzip.enabled";
String ENV_KEY_SESSION_ENABLED = "http.session.enabled";
String ENV_KEY_SESSION_KEY = "http.session.key";
String ENV_KEY_SESSION_TIMEOUT = "http.session.timeout";
String ENV_KEY_HTTP_CACHE_TIMEOUT = "http.cache.timeout";
String ENV_KEY_HTTP_REQUEST_COST = "http.request.cost";
String ENV_KEY_PAGE_404 = "mvc.view.404";
String ENV_KEY_PAGE_500 = "mvc.view.500";
String ENV_KEY_STATIC_DIRS = "mvc.statics";
String ENV_KEY_STATIC_LIST = "mvc.statics.show-list";
String ENV_KEY_STATIC_LIST = "static.show-list";
String ENV_KEY_STATIC_CACHE_SECONDS = "static.cache-seconds";
String ENV_KEY_TEMPLATE_PATH = "mvc.template.path";
String ENV_KEY_SERVER_ADDRESS = "server.address";
String ENV_KEY_SERVER_PORT = "server.port";
Expand All @@ -88,6 +85,7 @@ public interface BladeConst {

String REQUEST_TO_STATIC_ATTR = "_to_static";

String FAVICON_PATH = "/favicon.ico";
String NEW_LINE = "\r\n";

int BANNER_PADDING = 60;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public List<Route> getBefore(String path) {
.flatMap(Collection::stream)
.sorted(Comparator.comparingInt(Route::getSort))
.filter(route -> route.getHttpMethod() == HttpMethod.BEFORE)
.filter(route -> matchesPath(route.getPath(), cleanPath))
.filter(route -> matchesPath(route.getRewritePath(), cleanPath))
.collect(Collectors.toList());

this.giveMatch(path, collect);
Expand All @@ -178,7 +178,7 @@ public List<Route> getAfter(String path) {
.flatMap(Collection::stream)
.sorted(Comparator.comparingInt(Route::getSort))
.filter(route -> route.getHttpMethod() == HttpMethod.AFTER)
.filter(route -> matchesPath(route.getPath(), cleanPath))
.filter(route -> matchesPath(route.getRewritePath(), cleanPath))
.collect(Collectors.toList());

this.giveMatch(path, afters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum ResponseType {
XML(HttpConst.CONTENT_TYPE_XML),
TEXT(HttpConst.CONTENT_TYPE_TEXT),
HTML(HttpConst.CONTENT_TYPE_HTML),
VIEW(HttpConst.CONTENT_TYPE_HTML),
STREAM(HttpConst.CONTENT_TYPE_STREAM),
PREVIEW(""),
;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.hellokaton.blade.options;

import lombok.Getter;
import lombok.Setter;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
public class StaticOptions {

public static final int DEFAULT_CACHE_SECONDS = 86400 * 30;
public static final Set<String> DEFAULT_STATICS = new HashSet<>(
Arrays.asList("/favicon.ico", "/robots.txt", "/static", "/webjars/"));

private boolean showList;
private Set<String> paths = DEFAULT_STATICS;
private int cacheSeconds = DEFAULT_CACHE_SECONDS;

public static StaticOptions create() {
return new StaticOptions();
}

public StaticOptions showList() {
this.showList = true;
return this;
}

public StaticOptions addStatic(String staticPath) {
this.paths.add(staticPath);
return this;
}

public StaticOptions removeStatic(String staticPath) {
this.paths.remove(staticPath);
return this;
}

public StaticOptions cacheSeconds(int cacheSeconds) {
this.cacheSeconds = cacheSeconds;
return this;
}

}
Loading

0 comments on commit 2f1c463

Please sign in to comment.