Skip to content

Commit

Permalink
PathResourceManager may be configured with an ETagFunction
Browse files Browse the repository at this point in the history
PathResourceManagers already provided eight constructors, this
adds a builder to avoid more going forward.
  • Loading branch information
carterkozak committed Mar 4, 2017
1 parent c42d435 commit d94ad5c
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 15 deletions.
Expand Up @@ -35,12 +35,18 @@ public class PathResource implements RangeAwareResource {

private final Path file;
private final String path;
private final ETag eTag;
private final PathResourceManager manager;

public PathResource(final Path file, final PathResourceManager manager, String path) {
public PathResource(final Path file, final PathResourceManager manager, String path, ETag eTag) {
this.file = file;
this.path = path;
this.manager = manager;
this.eTag = eTag;
}

public PathResource(final Path file, final PathResourceManager manager, String path) {
this(file, manager, path, null);
}

@Override
Expand All @@ -64,7 +70,7 @@ public String getLastModifiedString() {

@Override
public ETag getETag() {
return null;
return eTag;
}

@Override
Expand Down
Expand Up @@ -3,6 +3,7 @@

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.util.ETag;
import org.xnio.FileChangeCallback;
import org.xnio.FileChangeEvent;
import org.xnio.FileSystemWatcher;
Expand All @@ -26,6 +27,13 @@
public class PathResourceManager implements ResourceManager {

private static final boolean DEFAULT_CHANGE_LISTENERS_ALLOWED = !Boolean.getBoolean("io.undertow.disable-file-system-watcher");
private static final long DEFAULT_TRANSFER_MIN_SIZE = 1024;
private static final ETagFunction NULL_ETAG_FUNCTION = new ETagFunction() {
@Override
public ETag generate(Path path) {
return null;
}
};

private final List<ResourceChangeListener> listeners = new ArrayList<>();

Expand Down Expand Up @@ -55,10 +63,12 @@ public class PathResourceManager implements ResourceManager {
*/
private final TreeSet<String> safePaths = new TreeSet<>();

private final ETagFunction eTagFunction;

private final boolean allowResourceChangeListeners;

public PathResourceManager(final Path base) {
this(base, 1024, true, false, null);
this(base, DEFAULT_TRANSFER_MIN_SIZE, true, false, null);
}

public PathResourceManager(final Path base, long transferMinSize) {
Expand Down Expand Up @@ -93,36 +103,48 @@ protected PathResourceManager(long transferMinSize, boolean caseSensitive, boole
}
this.safePaths.addAll(Arrays.asList(safePaths));
}
this.eTagFunction = NULL_ETAG_FUNCTION;
}

public PathResourceManager(final Path base, long transferMinSize, boolean caseSensitive, boolean followLinks, final String... safePaths) {
this(base, transferMinSize, caseSensitive, followLinks, DEFAULT_CHANGE_LISTENERS_ALLOWED, safePaths);
}

public PathResourceManager(final Path base, long transferMinSize, boolean caseSensitive, boolean followLinks, boolean allowResourceChangeListeners, final String... safePaths) {
this.allowResourceChangeListeners = allowResourceChangeListeners;
if (base == null) {
this(builder()
.setBase(base)
.setTransferMinSize(transferMinSize)
.setCaseSensitive(caseSensitive)
.setFollowLinks(followLinks)
.setAllowResourceChangeListeners(allowResourceChangeListeners)
.setSafePaths(safePaths));
}

private PathResourceManager(Builder builder) {
this.allowResourceChangeListeners = builder.allowResourceChangeListeners;
if (builder.base == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("base");
}
String basePath = base.normalize().toAbsolutePath().toString();
String basePath = builder.base.normalize().toAbsolutePath().toString();
if (!basePath.endsWith(File.separator)) {
basePath = basePath + File.separatorChar;
}
this.base = basePath;
this.transferMinSize = transferMinSize;
this.caseSensitive = caseSensitive;
this.followLinks = followLinks;
this.transferMinSize = builder.transferMinSize;
this.caseSensitive = builder.caseSensitive;
this.followLinks = builder.followLinks;
if (this.followLinks) {
if (safePaths == null) {
if (builder.safePaths == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("safePaths");
}
for (final String safePath : safePaths) {
for (final String safePath : builder.safePaths) {
if (safePath == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("safePaths");
}
}
this.safePaths.addAll(Arrays.asList(safePaths));
this.safePaths.addAll(Arrays.asList(builder.safePaths));
}
this.eTagFunction = builder.eTagFunction;
}

public Path getBasePath() {
Expand Down Expand Up @@ -340,16 +362,16 @@ protected PathResource getFileResource(final Path file, final String path, final
relative = relative.substring(1);
}
if (relative.equals(compare)) {
return new PathResource(file, this, path);
return new PathResource(file, this, path, eTagFunction.generate(file));
}
return null;
} else if (isFileSameCase(file, normalizedFile)) {
return new PathResource(file, this, path);
return new PathResource(file, this, path, eTagFunction.generate(file));
} else {
return null;
}
} else {
return new PathResource(file, this, path);
return new PathResource(file, this, path, eTagFunction.generate(file));
}
}

Expand All @@ -362,4 +384,72 @@ private SymlinkResult(boolean requiresCheck, Path path) {
this.path = path;
}
}

public interface ETagFunction {

/**
* Generates an {@link ETag} for the provided {@link Path}.
*
* @param path Path for which to generate an ETag
* @return ETag representing the provided path, or null
*/
ETag generate(Path path);
}

public static Builder builder() {
return new Builder();
}

public static final class Builder {

private Path base;
private long transferMinSize = DEFAULT_TRANSFER_MIN_SIZE;
private boolean caseSensitive = true;
private boolean followLinks = false;
private boolean allowResourceChangeListeners = DEFAULT_CHANGE_LISTENERS_ALLOWED;
private ETagFunction eTagFunction = NULL_ETAG_FUNCTION;
private String[] safePaths;

private Builder() {
}

public Builder setBase(Path base) {
this.base = base;
return this;
}

public Builder setTransferMinSize(long transferMinSize) {
this.transferMinSize = transferMinSize;
return this;
}

public Builder setCaseSensitive(boolean caseSensitive) {
this.caseSensitive = caseSensitive;
return this;
}

public Builder setFollowLinks(boolean followLinks) {
this.followLinks = followLinks;
return this;
}

public Builder setAllowResourceChangeListeners(boolean allowResourceChangeListeners) {
this.allowResourceChangeListeners = allowResourceChangeListeners;
return this;
}

public Builder setETagFunction(ETagFunction eTagFunction) {
this.eTagFunction = eTagFunction;
return this;
}

public Builder setSafePaths(String[] safePaths) {
this.safePaths = safePaths;
return this;
}

public ResourceManager build() {
return new PathResourceManager(this);
}
}
}
Expand Up @@ -2,6 +2,8 @@

import io.undertow.testutils.category.UnitTest;
import io.undertow.server.handlers.resource.PathResourceManager;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.util.ETag;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
Expand Down Expand Up @@ -68,4 +70,22 @@ public void testBaseDirInSymlink() throws Exception {
}

}

@Test
public void testETagFunction() throws Exception {
final String fileName = "page.html";
final Path rootPath = Paths.get(getClass().getResource(fileName).toURI()).getParent();
final ResourceManager resourceManager = PathResourceManager.builder()
.setBase(rootPath)
.setETagFunction(new PathResourceManager.ETagFunction() {
@Override
public ETag generate(Path path) {
return new ETag(true, path.getFileName().toString());
}
})
.build();
ETag expected = new ETag(true, fileName);
ETag actual = resourceManager.getResource("page.html").getETag();
Assert.assertEquals(expected, actual);
}
}

0 comments on commit d94ad5c

Please sign in to comment.