Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
restx.ResourcesRoute Maven / Gradle / Ivy
package restx;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
import restx.common.MoreResources;
import restx.http.HTTP;
import restx.http.HttpStatus;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Resources route allows to serves files from the classpath.
*
* Example:
* new ResourcesRoute("myResources", "web", "static")
* We will consider every urls matching /web/* will serve files into 'static' classpath's directory
* For instance, /web/foo/bar.json URL will serve classpath:static/foo/bar.json
*/
public class ResourcesRoute implements RestxRoute, RestxHandler {
/**
* Resource name, for toString only.
*/
private final String name;
/**
* Base restx path from which resources should be served.
* Sanitized will always a leading and trailing slash.
*/
private final String baseRestPath;
/**
* Base resource name path from which resources should be located in classpath.
* Sanitized with no leading slash, dots replaced with slashes and always a trailing slash.
*/
private final String baseResourcePath;
private final ImmutableMap aliases;
private final ImmutableList cachedResourcePolicies;
public static class ResourceInfo {
final String contentType;
final String path;
public ResourceInfo(String contentType, String path) {
this.contentType = contentType;
this.path = path;
}
public String getContentType() {
return contentType;
}
public String getPath() {
return path;
}
}
public static class CachedResourcePolicy {
final Predicate matcher;
final String cacheValue;
public CachedResourcePolicy(Predicate matcher, String cacheValue) {
this.matcher = matcher;
this.cacheValue = cacheValue;
}
public boolean matches(String contentType, String path) {
return matcher.apply(new ResourceInfo(contentType, path));
}
public String getCacheValue() {
return cacheValue;
}
}
public ResourcesRoute(String name, String baseRestPath, String baseResourcePath) {
this(name, baseRestPath, baseResourcePath, ImmutableMap.of());
}
public ResourcesRoute(String name, String baseRestPath, String baseResourcePath, ImmutableMap aliases) {
this(name, baseRestPath, baseResourcePath, aliases, Collections.emptyList());
}
public ResourcesRoute(String name, String baseRestPath, String baseResourcePath, ImmutableMap aliases, List cachedResourcePolicies) {
this.name = checkNotNull(name);
this.baseRestPath = ("/" + checkNotNull(baseRestPath) + "/").replaceAll("/+", "/");
this.baseResourcePath = this.ensureBaseResourcePathValid(baseResourcePath);
this.aliases = checkNotNull(aliases);
this.cachedResourcePolicies = ImmutableList.copyOf(cachedResourcePolicies);
}
protected String ensureBaseResourcePathValid(String baseResourcePath) {
String escapedBaseResourcePath = checkNotNull(baseResourcePath)
.replace('.', '/')
.replaceAll("^/", "")
.replaceAll("/$", "") + "/";
if("/".equals(escapedBaseResourcePath)){
throw new IllegalArgumentException("Please, avoid using '/' as ResourcesRoute's baseResourcePath as it represents serious security flaws (people will be able to read your classpath configuration files)");
}
return escapedBaseResourcePath;
}
@Override
public Optional match(RestxRequest req) {
if (req.getHttpMethod().equals("GET") && req.getRestxPath().startsWith(baseRestPath)) {
return Optional.of(new RestxHandlerMatch(
new StdRestxRequestMatch(baseRestPath + "*", req.getRestxPath()),
this));
} else {
return Optional.absent();
}
}
@Override
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException {
String relativePath = this.requestRelativePath(req);
relativePath = Optional.fromNullable(aliases.get(relativePath)).or(relativePath);
try {
URL resource = MoreResources.getResource(
baseResourcePath + relativePath, RestxContext.Modes.DEV.equals(ctx.getMode())
|| RestxContext.Modes.TEST.equals(ctx.getMode())
|| RestxContext.Modes.INFINIREST.equals(ctx.getMode())
);
serveCacheableResource(resp, resource, relativePath);
} catch (IllegalArgumentException e) {
notFound(resp, relativePath);
}
}
protected String requestRelativePath(RestxRequest req) {
return req.getRestxPath().substring(baseRestPath.length());
}
protected void serveCacheableResource(RestxResponse resp, URL resource, String relativePath) throws IOException {
String contentType = HTTP.getContentTypeFromExtension(relativePath).or("application/octet-stream");
ImmutableMap.Builder headers = ImmutableMap.builder();
Optional cachedResourcePolicy = cachePolicyMatching(contentType, relativePath);
if(cachedResourcePolicy.isPresent()) {
headers.put("Cache-Control", cachedResourcePolicy.get().getCacheValue());
}
serveResource(resp, resource, contentType, headers.build());
}
protected void serveResource(RestxResponse resp, URL resource, String contentType) throws IOException {
serveResource(resp, resource, contentType, ImmutableMap.of());
}
protected void serveResource(RestxResponse resp, URL resource, String contentType, Map headers) throws IOException {
resp.setLogLevel(RestxLogLevel.QUIET);
resp.setStatus(HttpStatus.OK);
for(Map.Entry headerEntry: headers.entrySet()) {
resp.setHeader(headerEntry.getKey(), headerEntry.getValue());
}
resp.setContentType(contentType);
Resources.asByteSource(resource).copyTo(resp.getOutputStream());
}
protected Optional cachePolicyMatching(String contentType, String path) {
for(CachedResourcePolicy cachedResourcePolicy : cachedResourcePolicies){
if(cachedResourcePolicy.matches(contentType, path)){
return Optional.of(cachedResourcePolicy);
}
}
return Optional.absent();
}
protected void notFound(RestxResponse resp, String relativePath) throws IOException {
resp.setStatus(HttpStatus.NOT_FOUND);
resp.setContentType("text/plain");
resp.getWriter().println("Resource route matched '" + this + "', but resource "
+ relativePath + " not found in " + baseResourcePath + ".");
}
public String getName() {
return name;
}
public String getBaseRestPath() {
return baseRestPath;
}
public String getBaseResourcePath() {
return baseResourcePath;
}
@Override
public String toString() {
return "GET " + baseRestPath + "* => " + name;
}
}