/*
* Copyright 2012 Jason Miller
*
* 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 jj.http.server;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.netty.handler.codec.http.HttpHeaderNames;
import jj.http.server.uri.Route;
import jj.http.server.uri.RouteMatch;
import jj.http.server.uri.URIMatch;
import jj.resource.ResourceFinder;
import jj.resource.ResourceLoader;
import jj.resource.ResourceThread;
/**
* <p>
* Serves resources that have simple lifecycle needs
*
* @author jason
*
*/
@Singleton
public class SimpleRouteProcessor implements RouteProcessor {
private final ResourceFinder resourceFinder;
private final ResourceLoader resourceLoader;
private final Map<String, Class<? extends ServableResource>> servableResources;
private final Map<Class<? extends ServableResource>, RouteProcessorConfiguration> configurationMap;
@Inject
SimpleRouteProcessor(
final ResourceFinder resourceFinder,
final ResourceLoader resourceLoader,
final Map<String, Class<? extends ServableResource>> servableResources,
final Map<Class<? extends ServableResource>, RouteProcessorConfiguration> configurationMap
) {
this.resourceFinder = resourceFinder;
this.resourceLoader = resourceLoader;
this.servableResources = servableResources;
this.configurationMap = configurationMap;
}
private ServableResource findResource(final Class<? extends ServableResource> resourceClass, final HttpServerRequest request) {
return resourceLoader.findResource(resourceClass, configurationMap.get(resourceClass).location(), request.uriMatch().path);
}
@ResourceThread
@Override
public <T extends ServableResource> T loadResource(final Class<T> resourceClass, final URIMatch uriMatch, final Route route) {
return resourceFinder.loadResource(resourceClass, configurationMap.get(resourceClass).location(), uriMatch.path);
}
@Override
public void process(final RouteMatch routeMatch, final HttpServerRequest request, final HttpServerResponse response) {
Class<? extends ServableResource> resourceClass = servableResources.get(routeMatch.resourceName());
assert resourceClass != null : "configured a route processor incorrectly";
ServableResource resource = findResource(resourceClass, request);
if (resource == null) {
// TODO - just use the task runner
resourceLoader.loadResource(
resourceClass,
configurationMap.get(resourceClass).location(),
request.uriMatch().path
).then(
new HttpServerTask("post-load, serving " + routeMatch.route()) {
@Override
protected void run() throws Exception {
ServableResource resource = findResource(resourceClass, request);
serve(resource, request, response);
}
}
);
} else {
serve(resource, request, response);
}
}
private void serve(final ServableResource resource, final HttpServerRequest request, final HttpServerResponse response) {
URIMatch match = request.uriMatch();
try {
// if we get nothing, they get nothing
if (resource == null) {
response.sendNotFound();
}
// if the e-tag matches our SHA, 304
else if (resource.sha1().equals(request.header(HttpHeaderNames.IF_NONE_MATCH))) {
response.sendNotModified(resource, match.versioned);
}
// if the URI was versioned, we send a cacheable resource if
// there was no SHA in the URL, or the SHA matches the resource
else if (
match.versioned &&
(match.sha1 == null || match.sha1.equals(resource.sha1()))
) {
response.sendCachableResource(resource);
}
// if the URI was versioned with a SHA that doesn't match our
// resource, redirect to the right URI
else if (match.versioned) {
response.sendTemporaryRedirect(resource);
}
// if the URI was not versioned, respond with an uncached resource
// (but with proper e-tags, if we loaded the resource
else {
response.sendUncachableResource(resource);
}
} catch (Exception e) {
response.error(e);
}
}
}